From 3d6123e69ab879c72ff489afc5bf93ef0b7a94ce Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 8 Mar 2005 18:53:11 +0000 Subject: [PATCH] Initial revision --- src/.cvsignore | 2 + src/Config | 450 ++ src/Families | 133 + src/Makefile | 15 + src/Makefile-armnommu | 18 + src/Makefile-e1 | 18 + src/Makefile-i386 | 18 + src/Makefile-ia64 | 18 + src/Makefile.main | 450 ++ src/README.pixify | 90 + src/arch/armnommu/Config | 23 + src/arch/armnommu/Makefile | 58 + src/arch/armnommu/core/arm_timer.c | 77 + src/arch/armnommu/core/etherboot.lds | 55 + src/arch/armnommu/core/lib1funcs.c | 105 + src/arch/armnommu/core/mem.c | 27 + src/arch/armnommu/core/raw_loader.c | 48 + src/arch/armnommu/core/serial.c | 66 + src/arch/armnommu/core/setjmp.S | 33 + src/arch/armnommu/core/start.S | 184 + src/arch/armnommu/drivers/net/p2001_eth.c | 525 ++ src/arch/armnommu/include/bits/byteswap.h | 47 + src/arch/armnommu/include/bits/cpu.h | 13 + src/arch/armnommu/include/bits/elf.h | 18 + src/arch/armnommu/include/bits/endian.h | 17 + src/arch/armnommu/include/bits/string.h | 11 + src/arch/armnommu/include/callbacks_arch.h | 1 + src/arch/armnommu/include/hardware.h | 179 + src/arch/armnommu/include/hooks.h | 25 + src/arch/armnommu/include/io.h | 27 + src/arch/armnommu/include/latch.h | 15 + src/arch/armnommu/include/limits.h | 43 + src/arch/armnommu/include/lxt971a.h | 30 + src/arch/armnommu/include/setjmp.h | 23 + src/arch/armnommu/include/stdint.h | 23 + src/arch/e1/Config | 7 + src/arch/e1/Makefile | 70 + src/arch/e1/Makefile.working | 67 + src/arch/e1/README | 80 + src/arch/e1/core/coff_loader.c | 176 + src/arch/e1/core/e132_xs.c | 70 + src/arch/e1/core/e1_timer.c | 94 + src/arch/e1/core/etherboot.lds | 126 + src/arch/e1/core/longjmp.c | 35 + src/arch/e1/core/memcmp.S | 54 + src/arch/e1/core/memcpy.S | 79 + src/arch/e1/core/memset.S | 47 + src/arch/e1/core/setjmp.c | 26 + src/arch/e1/core/start.S | 111 + src/arch/e1/core/strcmp.S | 76 + src/arch/e1/include/bits/byteswap.h | 39 + src/arch/e1/include/bits/cpu.h | 6 + src/arch/e1/include/bits/elf.h | 6 + src/arch/e1/include/bits/endian.h | 6 + src/arch/e1/include/bits/string.h | 35 + src/arch/e1/include/e132_xs_board.h | 22 + src/arch/e1/include/hooks.h | 9 + src/arch/e1/include/io.h | 210 + src/arch/e1/include/latch.h | 12 + src/arch/e1/include/limits.h | 34 + src/arch/e1/include/setjmp.h | 23 + src/arch/e1/include/stdint.h | 16 + src/arch/i386/Config | 131 + src/arch/i386/Makefile | 373 ++ src/arch/i386/core/aout_loader.c | 144 + src/arch/i386/core/callbacks.c | 107 + src/arch/i386/core/cpu.c | 86 + src/arch/i386/core/elf.c | 135 + src/arch/i386/core/etherboot.lds | 90 + src/arch/i386/core/etherboot.prefix.lds | 100 + src/arch/i386/core/freebsd_loader.c | 377 ++ src/arch/i386/core/hooks.c | 35 + src/arch/i386/core/i386_timer.c | 191 + src/arch/i386/core/init.S | 305 + src/arch/i386/core/multiboot_loader.c | 143 + src/arch/i386/core/pci_io.c | 352 ++ src/arch/i386/core/pic8259.c | 331 ++ src/arch/i386/core/prefixudata.lds | 8 + src/arch/i386/core/prefixzdata.lds | 8 + src/arch/i386/core/pxe_callbacks.c | 364 ++ src/arch/i386/core/pxe_loader.c | 94 + src/arch/i386/core/realmode.c | 148 + src/arch/i386/core/realmode_asm.S | 695 +++ src/arch/i386/core/start16.S | 285 + src/arch/i386/core/start16.lds | 8 + src/arch/i386/core/start16z.lds | 65 + src/arch/i386/core/start32.S | 767 +++ src/arch/i386/core/tagged_loader.c | 201 + src/arch/i386/core/video_subr.c | 94 + src/arch/i386/core/wince_loader.c | 273 + src/arch/i386/drivers/net/undi.c | 1458 +++++ src/arch/i386/drivers/net/undi.h | 178 + src/arch/i386/firmware/pcbios/basemem.c | 317 ++ src/arch/i386/firmware/pcbios/bios.c | 155 + src/arch/i386/firmware/pcbios/console.c | 85 + src/arch/i386/firmware/pcbios/e820mangler.S | 296 + src/arch/i386/firmware/pcbios/hidemem.c | 94 + src/arch/i386/firmware/pcbios/memsizes.c | 201 + src/arch/i386/include/bits/byteswap.h | 45 + src/arch/i386/include/bits/cpu.h | 243 + src/arch/i386/include/bits/elf.h | 91 + src/arch/i386/include/bits/elf_x.h | 5 + src/arch/i386/include/bits/eltorito.h | 3 + src/arch/i386/include/bits/endian.h | 9 + src/arch/i386/include/bits/string.h | 99 + src/arch/i386/include/callbacks_arch.h | 243 + src/arch/i386/include/hidemem.h | 21 + src/arch/i386/include/hooks.h | 9 + src/arch/i386/include/io.h | 246 + src/arch/i386/include/latch.h | 10 + src/arch/i386/include/limits.h | 59 + src/arch/i386/include/pic8259.h | 96 + src/arch/i386/include/pxe_callbacks.h | 33 + src/arch/i386/include/pxe_types.h | 35 + src/arch/i386/include/realmode.h | 124 + src/arch/i386/include/segoff.h | 41 + src/arch/i386/include/setjmp.h | 12 + src/arch/i386/include/stdint.h | 16 + src/arch/i386/include/vga.h | 228 + src/arch/i386/prefix/bImageprefix.S | 614 +++ src/arch/i386/prefix/boot1a.s | 410 ++ src/arch/i386/prefix/comprefix.S | 49 + src/arch/i386/prefix/elf_dprefix.S | 96 + src/arch/i386/prefix/elfprefix.S | 96 + src/arch/i386/prefix/exeprefix.S | 44 + src/arch/i386/prefix/floppyprefix.S | 359 ++ src/arch/i386/prefix/huf.lds | 6 + src/arch/i386/prefix/img.lds | 6 + src/arch/i386/prefix/liloprefix.S | 144 + src/arch/i386/prefix/lmelf_dprefix.S | 163 + src/arch/i386/prefix/lmelf_prefix.S | 163 + src/arch/i386/prefix/nullprefix.S | 16 + src/arch/i386/prefix/pxeprefix.S | 398 ++ src/arch/i386/prefix/romprefix.S | 416 ++ src/arch/i386/prefix/unhuf.S | 400 ++ src/arch/i386/prefix/unhuf.lds | 33 + src/arch/i386/prefix/unnrv2b.S | 129 + src/arch/ia64/Config | 22 + src/arch/ia64/Makefile | 125 + src/arch/ia64/core/__call.S | 68 + src/arch/ia64/core/efi.c | 1026 ++++ src/arch/ia64/core/etherboot.lds | 82 + src/arch/ia64/core/ia64_timer.c | 89 + src/arch/ia64/core/idiv32.S | 86 + src/arch/ia64/core/idiv64.S | 96 + src/arch/ia64/core/longjmp.S | 163 + src/arch/ia64/core/memmove.S | 244 + src/arch/ia64/core/memset.S | 133 + src/arch/ia64/core/pal.c | 84 + src/arch/ia64/core/pci_io.c | 62 + src/arch/ia64/core/reloc.S | 133 + src/arch/ia64/core/relocate_to.S | 92 + src/arch/ia64/core/sal.c | 278 + src/arch/ia64/core/setjmp.S | 173 + src/arch/ia64/core/start.S | 28 + src/arch/ia64/drivers/net/undi_nii.c | 1079 ++++ src/arch/ia64/include/bits/byteswap.h | 36 + src/arch/ia64/include/bits/cpu.h | 6 + src/arch/ia64/include/bits/elf.h | 11 + src/arch/ia64/include/bits/endian.h | 6 + src/arch/ia64/include/bits/string.h | 6 + src/arch/ia64/include/hooks.h | 12 + src/arch/ia64/include/io.h | 228 + src/arch/ia64/include/latch.h | 11 + src/arch/ia64/include/limits.h | 57 + src/arch/ia64/include/pal.h | 11 + src/arch/ia64/include/sal.h | 29 + src/arch/ia64/include/setjmp.h | 13 + src/arch/ia64/include/stdint.h | 16 + src/arch/ia64/prefix/apply_efi_prefix.pl | 63 + src/arch/ia64/prefix/apply_unnrv2b_prefix.pl | 198 + src/arch/ia64/prefix/efi_prefix.S | 195 + src/arch/ia64/prefix/efi_prefix.lds | 91 + src/arch/ia64/prefix/unnrv2b.S | 196 + src/arch/ia64/prefix/unnrv2b.lds | 28 + src/core/btext.c | 5196 ++++++++++++++++++ src/core/config.c | 161 + src/core/disk.c | 283 + src/core/dns_resolver.c | 419 ++ src/core/elf_loader.c | 658 +++ src/core/heap.c | 168 + src/core/i82365.c | 656 +++ src/core/isa_probe.c | 66 + src/core/isapnp.c | 382 ++ src/core/main.c | 529 ++ src/core/misc.c | 415 ++ src/core/nfs.c | 610 ++ src/core/nic.c | 1778 ++++++ src/core/osloader.c | 365 ++ src/core/pc_kbd.c | 108 + src/core/pci.c | 337 ++ src/core/pci_probe.c | 70 + src/core/pcmcia.c | 269 + src/core/proto_eth_slow.c | 407 ++ src/core/proto_http.c | 206 + src/core/proto_slam.c | 541 ++ src/core/proto_tftm.c | 491 ++ src/core/pxe_export.c | 1424 +++++ src/core/relocate.c | 103 + src/core/serial.c | 236 + src/core/string.c | 540 ++ src/core/timer.c | 30 + src/core/vsprintf.c | 166 + src/drivers/disk/filo.c | 58 + src/drivers/disk/floppy.c | 88 + src/drivers/disk/ide_disk.c | 893 +++ src/drivers/disk/pc_floppy.c | 1151 ++++ src/drivers/net/3c509.c | 671 +++ src/drivers/net/3c509.h | 399 ++ src/drivers/net/3c515.c | 814 +++ src/drivers/net/3c515.txt | 31 + src/drivers/net/3c595.c | 550 ++ src/drivers/net/3c595.h | 435 ++ src/drivers/net/3c90x.c | 996 ++++ src/drivers/net/3c90x.txt | 307 ++ src/drivers/net/cs89x0.c | 720 +++ src/drivers/net/cs89x0.h | 461 ++ src/drivers/net/cs89x0.txt | 26 + src/drivers/net/davicom.c | 720 +++ src/drivers/net/depca.c | 794 +++ src/drivers/net/dmfe.c | 1230 +++++ src/drivers/net/e1000.c | 3713 +++++++++++++ src/drivers/net/e1000_hw.h | 2058 +++++++ src/drivers/net/eepro.c | 625 +++ src/drivers/net/eepro100.c | 841 +++ src/drivers/net/epic100.c | 520 ++ src/drivers/net/epic100.h | 188 + src/drivers/net/forcedeth.c | 1039 ++++ src/drivers/net/hfa384x.h | 2744 +++++++++ src/drivers/net/mtd80x.c | 1096 ++++ src/drivers/net/natsemi.c | 780 +++ src/drivers/net/ns83820.c | 1020 ++++ src/drivers/net/ns8390.c | 1016 ++++ src/drivers/net/ns8390.h | 238 + src/drivers/net/p80211hdr.h | 262 + src/drivers/net/pcnet32.c | 1004 ++++ src/drivers/net/pnic.c | 267 + src/drivers/net/pnic_api.h | 59 + src/drivers/net/prism2.c | 948 ++++ src/drivers/net/prism2_pci.c | 32 + src/drivers/net/prism2_plx.c | 42 + src/drivers/net/r8169.c | 854 +++ src/drivers/net/rtl8139.c | 551 ++ src/drivers/net/sis900.c | 1271 +++++ src/drivers/net/sis900.h | 380 ++ src/drivers/net/sis900.txt | 91 + src/drivers/net/sk_g16.c | 1189 ++++ src/drivers/net/sk_g16.h | 171 + src/drivers/net/skel.c | 200 + src/drivers/net/smc9000.c | 544 ++ src/drivers/net/smc9000.h | 205 + src/drivers/net/sundance.c | 891 +++ src/drivers/net/tg3.c | 3394 ++++++++++++ src/drivers/net/tg3.h | 2211 ++++++++ src/drivers/net/tlan.c | 1722 ++++++ src/drivers/net/tlan.h | 536 ++ src/drivers/net/tulip.c | 2082 +++++++ src/drivers/net/tulip.txt | 53 + src/drivers/net/via-rhine.c | 1426 +++++ src/drivers/net/w89c840.c | 958 ++++ src/drivers/net/wlan_compat.h | 575 ++ src/filo/Config | 68 + src/filo/README.filo | 125 + src/filo/README.filo_in_etherboot | 48 + src/filo/README.usb | 15 + src/filo/drivers/ide_x.c | 1129 ++++ src/filo/fs/blockdev.c | 383 ++ src/filo/fs/eltorito.c | 147 + src/filo/fs/fat.h | 100 + src/filo/fs/filesys.h | 233 + src/filo/fs/fsys_ext2fs.c | 779 +++ src/filo/fs/fsys_fat.c | 494 ++ src/filo/fs/fsys_iso9660.c | 348 ++ src/filo/fs/fsys_jfs.c | 403 ++ src/filo/fs/fsys_minix.c | 534 ++ src/filo/fs/fsys_reiserfs.c | 1239 +++++ src/filo/fs/fsys_xfs.c | 624 +++ src/filo/fs/iso9660.h | 168 + src/filo/fs/jfs.h | 601 ++ src/filo/fs/shared.h | 1 + src/filo/fs/vfs.c | 193 + src/filo/fs/xfs.h | 546 ++ src/filo/i386/context.c | 125 + src/filo/i386/context.h | 50 + src/filo/i386/linux_load.c | 629 +++ src/filo/i386/multiboot.c | 129 + src/filo/i386/segment.c | 48 + src/filo/i386/segment.h | 29 + src/filo/i386/switch.S | 116 + src/filo/i386/sys_info.c | 29 + src/filo/main/console_x.c | 74 + src/filo/main/elfload.c | 398 ++ src/filo/main/elfnote.c | 153 + src/filo/main/filo_x.c | 129 + src/filo/main/lib.c | 56 + src/filo/main/linuxbios_x.c | 124 + src/filo/main/malloc_x.c | 45 + src/filo/main/pci_x.c | 124 + src/filo/main/printf_x.c | 400 ++ src/filo/usb/debug_x.c | 433 ++ src/filo/usb/debug_x.h | 18 + src/filo/usb/ohci.c | 1437 +++++ src/filo/usb/ohci.h | 316 ++ src/filo/usb/scsi.h | 226 + src/filo/usb/scsi_cmds.c | 512 ++ src/filo/usb/scsi_cmds.h | 14 + src/filo/usb/uhci.c | 1143 ++++ src/filo/usb/uhci.h | 175 + src/filo/usb/usb.c | 803 +++ src/filo/usb/usb.h | 435 ++ src/filo/usb/usb_scsi_low.c | 172 + src/filo/usb/usb_scsi_low.h | 10 + src/filo/usb/usb_x.c | 163 + src/firmware/linuxbios/linuxbios.c | 386 ++ src/firmware/linuxbios/linuxbios_tables.h | 183 + src/genrules.pl | 376 ++ src/include/.cvsignore | 1 + src/include/big_bswap.h | 17 + src/include/bootp.h | 230 + src/include/btext.h | 76 + src/include/byteswap.h | 20 + src/include/callbacks.h | 45 + src/include/coff.h | 73 + src/include/cpu.h | 6 + src/include/debug.h | 28 + src/include/dev.h | 126 + src/include/disk.h | 53 + src/include/dns_resolver.h | 58 + src/include/elf.h | 234 + src/include/elf_boot.h | 84 + src/include/endian.h | 19 + src/include/etherboot.h | 459 ++ src/include/fs.h | 41 + src/include/http.h | 7 + src/include/i82365.h | 450 ++ src/include/if_arp.h | 23 + src/include/if_ether.h | 27 + src/include/igmp.h | 23 + src/include/in.h | 21 + src/include/ip.h | 17 + src/include/isa.h | 32 + src/include/isapnp.h | 124 + src/include/lib.h | 42 + src/include/little_bswap.h | 17 + src/include/mii.h | 105 + src/include/nfs.h | 63 + src/include/nic.h | 49 + src/include/osdep.h | 43 + src/include/pc_kbd.h | 7 + src/include/pci.h | 362 ++ src/include/pci_ids.h | 342 ++ src/include/pcmcia-opts.h | 23 + src/include/pcmcia.h | 157 + src/include/pxe.h | 929 ++++ src/include/pxe_export.h | 61 + src/include/string.h | 68 + src/include/sys_info.h | 33 + src/include/tcp.h | 35 + src/include/tftp.h | 77 + src/include/timer.h | 62 + src/include/udp.h | 27 + src/util/catrom.pl | 48 + src/util/disrom.pl | 114 + src/util/geniso | 56 + src/util/genliso | 85 + src/util/get-pci-ids | 135 + src/util/lzhuf.c | 764 +++ src/util/makelilo.pl | 40 + src/util/makerom.pl | 226 + src/util/modrom.pl | 226 + src/util/nrv2b.c | 1501 +++++ src/util/swapdevids.pl | 49 + src/util/zfilelen.pl | 8 + 373 files changed, 114041 insertions(+) create mode 100644 src/.cvsignore create mode 100644 src/Config create mode 100644 src/Families create mode 100644 src/Makefile create mode 100644 src/Makefile-armnommu create mode 100644 src/Makefile-e1 create mode 100644 src/Makefile-i386 create mode 100644 src/Makefile-ia64 create mode 100644 src/Makefile.main create mode 100644 src/README.pixify create mode 100644 src/arch/armnommu/Config create mode 100644 src/arch/armnommu/Makefile create mode 100644 src/arch/armnommu/core/arm_timer.c create mode 100644 src/arch/armnommu/core/etherboot.lds create mode 100644 src/arch/armnommu/core/lib1funcs.c create mode 100644 src/arch/armnommu/core/mem.c create mode 100644 src/arch/armnommu/core/raw_loader.c create mode 100644 src/arch/armnommu/core/serial.c create mode 100644 src/arch/armnommu/core/setjmp.S create mode 100644 src/arch/armnommu/core/start.S create mode 100644 src/arch/armnommu/drivers/net/p2001_eth.c create mode 100644 src/arch/armnommu/include/bits/byteswap.h create mode 100644 src/arch/armnommu/include/bits/cpu.h create mode 100644 src/arch/armnommu/include/bits/elf.h create mode 100644 src/arch/armnommu/include/bits/endian.h create mode 100644 src/arch/armnommu/include/bits/string.h create mode 100644 src/arch/armnommu/include/callbacks_arch.h create mode 100644 src/arch/armnommu/include/hardware.h create mode 100644 src/arch/armnommu/include/hooks.h create mode 100644 src/arch/armnommu/include/io.h create mode 100644 src/arch/armnommu/include/latch.h create mode 100644 src/arch/armnommu/include/limits.h create mode 100644 src/arch/armnommu/include/lxt971a.h create mode 100644 src/arch/armnommu/include/setjmp.h create mode 100644 src/arch/armnommu/include/stdint.h create mode 100644 src/arch/e1/Config create mode 100644 src/arch/e1/Makefile create mode 100644 src/arch/e1/Makefile.working create mode 100644 src/arch/e1/README create mode 100644 src/arch/e1/core/coff_loader.c create mode 100644 src/arch/e1/core/e132_xs.c create mode 100644 src/arch/e1/core/e1_timer.c create mode 100644 src/arch/e1/core/etherboot.lds create mode 100644 src/arch/e1/core/longjmp.c create mode 100644 src/arch/e1/core/memcmp.S create mode 100644 src/arch/e1/core/memcpy.S create mode 100644 src/arch/e1/core/memset.S create mode 100644 src/arch/e1/core/setjmp.c create mode 100644 src/arch/e1/core/start.S create mode 100644 src/arch/e1/core/strcmp.S create mode 100644 src/arch/e1/include/bits/byteswap.h create mode 100644 src/arch/e1/include/bits/cpu.h create mode 100644 src/arch/e1/include/bits/elf.h create mode 100644 src/arch/e1/include/bits/endian.h create mode 100644 src/arch/e1/include/bits/string.h create mode 100644 src/arch/e1/include/e132_xs_board.h create mode 100644 src/arch/e1/include/hooks.h create mode 100644 src/arch/e1/include/io.h create mode 100644 src/arch/e1/include/latch.h create mode 100644 src/arch/e1/include/limits.h create mode 100644 src/arch/e1/include/setjmp.h create mode 100644 src/arch/e1/include/stdint.h create mode 100644 src/arch/i386/Config create mode 100644 src/arch/i386/Makefile create mode 100644 src/arch/i386/core/aout_loader.c create mode 100644 src/arch/i386/core/callbacks.c create mode 100644 src/arch/i386/core/cpu.c create mode 100644 src/arch/i386/core/elf.c create mode 100644 src/arch/i386/core/etherboot.lds create mode 100644 src/arch/i386/core/etherboot.prefix.lds create mode 100644 src/arch/i386/core/freebsd_loader.c create mode 100644 src/arch/i386/core/hooks.c create mode 100644 src/arch/i386/core/i386_timer.c create mode 100644 src/arch/i386/core/init.S create mode 100644 src/arch/i386/core/multiboot_loader.c create mode 100644 src/arch/i386/core/pci_io.c create mode 100644 src/arch/i386/core/pic8259.c create mode 100644 src/arch/i386/core/prefixudata.lds create mode 100644 src/arch/i386/core/prefixzdata.lds create mode 100644 src/arch/i386/core/pxe_callbacks.c create mode 100644 src/arch/i386/core/pxe_loader.c create mode 100644 src/arch/i386/core/realmode.c create mode 100644 src/arch/i386/core/realmode_asm.S create mode 100644 src/arch/i386/core/start16.S create mode 100644 src/arch/i386/core/start16.lds create mode 100644 src/arch/i386/core/start16z.lds create mode 100644 src/arch/i386/core/start32.S create mode 100644 src/arch/i386/core/tagged_loader.c create mode 100644 src/arch/i386/core/video_subr.c create mode 100644 src/arch/i386/core/wince_loader.c create mode 100644 src/arch/i386/drivers/net/undi.c create mode 100644 src/arch/i386/drivers/net/undi.h create mode 100644 src/arch/i386/firmware/pcbios/basemem.c create mode 100644 src/arch/i386/firmware/pcbios/bios.c create mode 100644 src/arch/i386/firmware/pcbios/console.c create mode 100644 src/arch/i386/firmware/pcbios/e820mangler.S create mode 100644 src/arch/i386/firmware/pcbios/hidemem.c create mode 100644 src/arch/i386/firmware/pcbios/memsizes.c create mode 100644 src/arch/i386/include/bits/byteswap.h create mode 100644 src/arch/i386/include/bits/cpu.h create mode 100644 src/arch/i386/include/bits/elf.h create mode 100644 src/arch/i386/include/bits/elf_x.h create mode 100644 src/arch/i386/include/bits/eltorito.h create mode 100644 src/arch/i386/include/bits/endian.h create mode 100644 src/arch/i386/include/bits/string.h create mode 100644 src/arch/i386/include/callbacks_arch.h create mode 100644 src/arch/i386/include/hidemem.h create mode 100644 src/arch/i386/include/hooks.h create mode 100644 src/arch/i386/include/io.h create mode 100644 src/arch/i386/include/latch.h create mode 100644 src/arch/i386/include/limits.h create mode 100644 src/arch/i386/include/pic8259.h create mode 100644 src/arch/i386/include/pxe_callbacks.h create mode 100644 src/arch/i386/include/pxe_types.h create mode 100644 src/arch/i386/include/realmode.h create mode 100644 src/arch/i386/include/segoff.h create mode 100644 src/arch/i386/include/setjmp.h create mode 100644 src/arch/i386/include/stdint.h create mode 100644 src/arch/i386/include/vga.h create mode 100644 src/arch/i386/prefix/bImageprefix.S create mode 100644 src/arch/i386/prefix/boot1a.s create mode 100644 src/arch/i386/prefix/comprefix.S create mode 100644 src/arch/i386/prefix/elf_dprefix.S create mode 100644 src/arch/i386/prefix/elfprefix.S create mode 100755 src/arch/i386/prefix/exeprefix.S create mode 100644 src/arch/i386/prefix/floppyprefix.S create mode 100644 src/arch/i386/prefix/huf.lds create mode 100644 src/arch/i386/prefix/img.lds create mode 100644 src/arch/i386/prefix/liloprefix.S create mode 100644 src/arch/i386/prefix/lmelf_dprefix.S create mode 100644 src/arch/i386/prefix/lmelf_prefix.S create mode 100644 src/arch/i386/prefix/nullprefix.S create mode 100644 src/arch/i386/prefix/pxeprefix.S create mode 100644 src/arch/i386/prefix/romprefix.S create mode 100644 src/arch/i386/prefix/unhuf.S create mode 100644 src/arch/i386/prefix/unhuf.lds create mode 100644 src/arch/i386/prefix/unnrv2b.S create mode 100644 src/arch/ia64/Config create mode 100644 src/arch/ia64/Makefile create mode 100644 src/arch/ia64/core/__call.S create mode 100644 src/arch/ia64/core/efi.c create mode 100644 src/arch/ia64/core/etherboot.lds create mode 100644 src/arch/ia64/core/ia64_timer.c create mode 100644 src/arch/ia64/core/idiv32.S create mode 100644 src/arch/ia64/core/idiv64.S create mode 100644 src/arch/ia64/core/longjmp.S create mode 100644 src/arch/ia64/core/memmove.S create mode 100644 src/arch/ia64/core/memset.S create mode 100644 src/arch/ia64/core/pal.c create mode 100644 src/arch/ia64/core/pci_io.c create mode 100644 src/arch/ia64/core/reloc.S create mode 100644 src/arch/ia64/core/relocate_to.S create mode 100644 src/arch/ia64/core/sal.c create mode 100644 src/arch/ia64/core/setjmp.S create mode 100644 src/arch/ia64/core/start.S create mode 100644 src/arch/ia64/drivers/net/undi_nii.c create mode 100644 src/arch/ia64/include/bits/byteswap.h create mode 100644 src/arch/ia64/include/bits/cpu.h create mode 100644 src/arch/ia64/include/bits/elf.h create mode 100644 src/arch/ia64/include/bits/endian.h create mode 100644 src/arch/ia64/include/bits/string.h create mode 100644 src/arch/ia64/include/hooks.h create mode 100644 src/arch/ia64/include/io.h create mode 100644 src/arch/ia64/include/latch.h create mode 100644 src/arch/ia64/include/limits.h create mode 100644 src/arch/ia64/include/pal.h create mode 100644 src/arch/ia64/include/sal.h create mode 100644 src/arch/ia64/include/setjmp.h create mode 100644 src/arch/ia64/include/stdint.h create mode 100755 src/arch/ia64/prefix/apply_efi_prefix.pl create mode 100644 src/arch/ia64/prefix/apply_unnrv2b_prefix.pl create mode 100644 src/arch/ia64/prefix/efi_prefix.S create mode 100644 src/arch/ia64/prefix/efi_prefix.lds create mode 100644 src/arch/ia64/prefix/unnrv2b.S create mode 100644 src/arch/ia64/prefix/unnrv2b.lds create mode 100644 src/core/btext.c create mode 100644 src/core/config.c create mode 100644 src/core/disk.c create mode 100644 src/core/dns_resolver.c create mode 100644 src/core/elf_loader.c create mode 100644 src/core/heap.c create mode 100644 src/core/i82365.c create mode 100644 src/core/isa_probe.c create mode 100644 src/core/isapnp.c create mode 100644 src/core/main.c create mode 100644 src/core/misc.c create mode 100644 src/core/nfs.c create mode 100644 src/core/nic.c create mode 100644 src/core/osloader.c create mode 100644 src/core/pc_kbd.c create mode 100644 src/core/pci.c create mode 100644 src/core/pci_probe.c create mode 100644 src/core/pcmcia.c create mode 100644 src/core/proto_eth_slow.c create mode 100644 src/core/proto_http.c create mode 100644 src/core/proto_slam.c create mode 100644 src/core/proto_tftm.c create mode 100644 src/core/pxe_export.c create mode 100644 src/core/relocate.c create mode 100644 src/core/serial.c create mode 100644 src/core/string.c create mode 100644 src/core/timer.c create mode 100644 src/core/vsprintf.c create mode 100644 src/drivers/disk/filo.c create mode 100644 src/drivers/disk/floppy.c create mode 100644 src/drivers/disk/ide_disk.c create mode 100644 src/drivers/disk/pc_floppy.c create mode 100644 src/drivers/net/3c509.c create mode 100644 src/drivers/net/3c509.h create mode 100644 src/drivers/net/3c515.c create mode 100644 src/drivers/net/3c515.txt create mode 100644 src/drivers/net/3c595.c create mode 100644 src/drivers/net/3c595.h create mode 100644 src/drivers/net/3c90x.c create mode 100644 src/drivers/net/3c90x.txt create mode 100644 src/drivers/net/cs89x0.c create mode 100644 src/drivers/net/cs89x0.h create mode 100644 src/drivers/net/cs89x0.txt create mode 100644 src/drivers/net/davicom.c create mode 100644 src/drivers/net/depca.c create mode 100644 src/drivers/net/dmfe.c create mode 100644 src/drivers/net/e1000.c create mode 100644 src/drivers/net/e1000_hw.h create mode 100644 src/drivers/net/eepro.c create mode 100644 src/drivers/net/eepro100.c create mode 100644 src/drivers/net/epic100.c create mode 100644 src/drivers/net/epic100.h create mode 100644 src/drivers/net/forcedeth.c create mode 100644 src/drivers/net/hfa384x.h create mode 100644 src/drivers/net/mtd80x.c create mode 100644 src/drivers/net/natsemi.c create mode 100755 src/drivers/net/ns83820.c create mode 100644 src/drivers/net/ns8390.c create mode 100644 src/drivers/net/ns8390.h create mode 100644 src/drivers/net/p80211hdr.h create mode 100644 src/drivers/net/pcnet32.c create mode 100644 src/drivers/net/pnic.c create mode 100644 src/drivers/net/pnic_api.h create mode 100644 src/drivers/net/prism2.c create mode 100644 src/drivers/net/prism2_pci.c create mode 100644 src/drivers/net/prism2_plx.c create mode 100644 src/drivers/net/r8169.c create mode 100644 src/drivers/net/rtl8139.c create mode 100644 src/drivers/net/sis900.c create mode 100644 src/drivers/net/sis900.h create mode 100644 src/drivers/net/sis900.txt create mode 100644 src/drivers/net/sk_g16.c create mode 100644 src/drivers/net/sk_g16.h create mode 100644 src/drivers/net/skel.c create mode 100644 src/drivers/net/smc9000.c create mode 100644 src/drivers/net/smc9000.h create mode 100644 src/drivers/net/sundance.c create mode 100644 src/drivers/net/tg3.c create mode 100644 src/drivers/net/tg3.h create mode 100644 src/drivers/net/tlan.c create mode 100644 src/drivers/net/tlan.h create mode 100644 src/drivers/net/tulip.c create mode 100644 src/drivers/net/tulip.txt create mode 100644 src/drivers/net/via-rhine.c create mode 100644 src/drivers/net/w89c840.c create mode 100644 src/drivers/net/wlan_compat.h create mode 100644 src/filo/Config create mode 100644 src/filo/README.filo create mode 100644 src/filo/README.filo_in_etherboot create mode 100644 src/filo/README.usb create mode 100644 src/filo/drivers/ide_x.c create mode 100644 src/filo/fs/blockdev.c create mode 100644 src/filo/fs/eltorito.c create mode 100644 src/filo/fs/fat.h create mode 100644 src/filo/fs/filesys.h create mode 100644 src/filo/fs/fsys_ext2fs.c create mode 100644 src/filo/fs/fsys_fat.c create mode 100644 src/filo/fs/fsys_iso9660.c create mode 100644 src/filo/fs/fsys_jfs.c create mode 100644 src/filo/fs/fsys_minix.c create mode 100644 src/filo/fs/fsys_reiserfs.c create mode 100644 src/filo/fs/fsys_xfs.c create mode 100644 src/filo/fs/iso9660.h create mode 100644 src/filo/fs/jfs.h create mode 100644 src/filo/fs/shared.h create mode 100644 src/filo/fs/vfs.c create mode 100644 src/filo/fs/xfs.h create mode 100644 src/filo/i386/context.c create mode 100644 src/filo/i386/context.h create mode 100644 src/filo/i386/linux_load.c create mode 100644 src/filo/i386/multiboot.c create mode 100644 src/filo/i386/segment.c create mode 100644 src/filo/i386/segment.h create mode 100644 src/filo/i386/switch.S create mode 100644 src/filo/i386/sys_info.c create mode 100644 src/filo/main/console_x.c create mode 100644 src/filo/main/elfload.c create mode 100644 src/filo/main/elfnote.c create mode 100644 src/filo/main/filo_x.c create mode 100644 src/filo/main/lib.c create mode 100644 src/filo/main/linuxbios_x.c create mode 100644 src/filo/main/malloc_x.c create mode 100644 src/filo/main/pci_x.c create mode 100644 src/filo/main/printf_x.c create mode 100644 src/filo/usb/debug_x.c create mode 100644 src/filo/usb/debug_x.h create mode 100644 src/filo/usb/ohci.c create mode 100644 src/filo/usb/ohci.h create mode 100644 src/filo/usb/scsi.h create mode 100644 src/filo/usb/scsi_cmds.c create mode 100644 src/filo/usb/scsi_cmds.h create mode 100644 src/filo/usb/uhci.c create mode 100644 src/filo/usb/uhci.h create mode 100644 src/filo/usb/usb.c create mode 100644 src/filo/usb/usb.h create mode 100644 src/filo/usb/usb_scsi_low.c create mode 100644 src/filo/usb/usb_scsi_low.h create mode 100644 src/filo/usb/usb_x.c create mode 100644 src/firmware/linuxbios/linuxbios.c create mode 100644 src/firmware/linuxbios/linuxbios_tables.h create mode 100755 src/genrules.pl create mode 100644 src/include/.cvsignore create mode 100644 src/include/big_bswap.h create mode 100644 src/include/bootp.h create mode 100644 src/include/btext.h create mode 100644 src/include/byteswap.h create mode 100644 src/include/callbacks.h create mode 100644 src/include/coff.h create mode 100644 src/include/cpu.h create mode 100644 src/include/debug.h create mode 100644 src/include/dev.h create mode 100644 src/include/disk.h create mode 100644 src/include/dns_resolver.h create mode 100644 src/include/elf.h create mode 100644 src/include/elf_boot.h create mode 100644 src/include/endian.h create mode 100644 src/include/etherboot.h create mode 100644 src/include/fs.h create mode 100644 src/include/http.h create mode 100644 src/include/i82365.h create mode 100644 src/include/if_arp.h create mode 100644 src/include/if_ether.h create mode 100644 src/include/igmp.h create mode 100644 src/include/in.h create mode 100644 src/include/ip.h create mode 100644 src/include/isa.h create mode 100644 src/include/isapnp.h create mode 100644 src/include/lib.h create mode 100644 src/include/little_bswap.h create mode 100644 src/include/mii.h create mode 100644 src/include/nfs.h create mode 100644 src/include/nic.h create mode 100644 src/include/osdep.h create mode 100644 src/include/pc_kbd.h create mode 100644 src/include/pci.h create mode 100644 src/include/pci_ids.h create mode 100644 src/include/pcmcia-opts.h create mode 100644 src/include/pcmcia.h create mode 100644 src/include/pxe.h create mode 100644 src/include/pxe_export.h create mode 100644 src/include/string.h create mode 100644 src/include/sys_info.h create mode 100644 src/include/tcp.h create mode 100644 src/include/tftp.h create mode 100644 src/include/timer.h create mode 100644 src/include/udp.h create mode 100755 src/util/catrom.pl create mode 100755 src/util/disrom.pl create mode 100755 src/util/geniso create mode 100755 src/util/genliso create mode 100755 src/util/get-pci-ids create mode 100644 src/util/lzhuf.c create mode 100755 src/util/makelilo.pl create mode 100755 src/util/makerom.pl create mode 100755 src/util/modrom.pl create mode 100644 src/util/nrv2b.c create mode 100755 src/util/swapdevids.pl create mode 100755 src/util/zfilelen.pl diff --git a/src/.cvsignore b/src/.cvsignore new file mode 100644 index 00000000..2e0d941a --- /dev/null +++ b/src/.cvsignore @@ -0,0 +1,2 @@ +bin +gcccheck diff --git a/src/Config b/src/Config new file mode 100644 index 00000000..7be549b1 --- /dev/null +++ b/src/Config @@ -0,0 +1,450 @@ +# +# 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= +# -DDHCP_CLIENT_ID_TYPE= +# Specify a RFC2132 Client Identifier option, length and type. +# Requires DHCP support. +# -DDHCP_USER_CLASS=\"UserClass\" +# -DDHCP_USER_CLASS_LEN= +# 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: +# +# -DTAGGED_IMAGE +# Add tagged image kernel boot support (recommended). +# -DAOUT_IMAGE +# Add a.out kernel boot support (generic). +# -DELF_IMAGE +# Add generic ELF kernel boot support (recommended). +# -DEL64F_IMAGE +# Add generic ELF64 kernel boot support (useful for > 4GB disks). +# -DWINCE_IMAGE +# Add the ability to boot WINCE.... now only sis630 OK! +# -DPXE_IMAGE +# Add the ability to boot PXE NBPs. Requires +# PXE_EXPORT. Currently not supported on +# anything other than i386 +# -DFREEBSD_PXEEMU +# Add the ability to boot PXE images... only FreeBSD supported +# -DIMAGE_MULTIBOOT +# Add Multiboot image support (currently only +# for ELF images). +# Without this, generic ELF support is selected. +# -DIMAGE_FREEBSD +# Add FreeBSD image loading support (requires at least +# -DAOUT_IMAGE and/or -DELF_IMAGE). +# -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 +# -DDNS_RESOLVER +# Support for resolving hostnames in bootfile name (experimental) +# -DDOWNLOAD_PROTO_TFTP +# If defined, includes TFTP support (recommended). +# -DDOWNLOAD_PROTO_NFS +# If defined, includes NFS support. +# -DDEFAULT_PROTO_NFS +# If defined, makes NFS the default protocol instead +# of TFTP. Requires DOWNLOAD_PROTO_NFS. +# -DDOWNLOAD_PROTO_SLAM +# If defined, includes Scalable Local Area Multicast +# support. +# -DDOWNLOAD_PROTO_TFTM +# If defined, includes TFTP Multicast mode support. +# -DDOWNLOAD_PROTO_HTTP +# If defined, includes HTTP support. +# +# Console options: +# +# -DCONSOLE_FIRMWARE +# Set for firmware/BIOS provided (default if nothing else is set). +# Normally this is shows up on your CRT. +# -DCONSOLE_SERIAL +# Set for serial console. +# -DCONSOLE_DUAL +# Both of the above +# -DCONSOLE_DIRECT_VGA +# Set for direct VGA console (only for x86). +# -DCOMCONSOLE +# Set port, e.g. 0x3F8. +# -DCONSPEED +# Set speed, e.g. 57600. +# -DCOMPARM +# Set Line Control Register value for data bits, stop +# bits and parity. See a National Semiconditor 8250/ +# 16450/16550 data sheet for bit meanings. +# If undefined, defaults to 0x03 = 8N1. +# -DCOMPRESERVE +# Ignore COMSPEED and COMPARAM and instead preserve +# the com port parameters from the previous user +# of the com port. Examples of previous user are a BIOS +# that implements console redirection, lilo and LinuxBIOS. +# This makes it trivial to keep the serial port +# speed setting in sync between multiple users. +# You set the speed in the first user and the +# rest follow along. +# +# 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: +# +# -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. +# -DBUILD_SERIAL +# Include an auto-incrementing build number in +# the Etherboot welcome message. Useful when +# developing, to be sure that the file you +# compiled is the one you're currently testing. +# -DBUILD_ID +# Include a build ID string in the Etherboot +# welcome message. Useful when developing, if +# you have multiple builds with different +# configurations and want to check you're +# running the one you think you are. Requires +# -DBUILD_SERIAL. +# +# BUS options: +# +# -DCONFIG_PCI +# Include support for devices using the pci bus. +# -DCONFIG_ISA +# Include support for devices using isa bus. +# -DCONFIG_PCMCIA +# Include support for PCMCIA in general *development* +# @/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. + + +# Select which buses etherboot should support +CFLAGS+= -DCONFIG_PCI -DCONFIG_ISA +# CFLAGS+= -DCONFIG_PCMCIA + +# 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 + +# for btext console support +# CFLAGS+= -DCONSOLE_BTEXT +# for direct PC kbd support +# CFLAGS+= -DCONSOLE_PC_KBD +# Set to enable FILO support +# for FILO support it will make main call pci_init +# INCLUDE_FILO=y +ifdef INCLUDE_FILO +CFLAGS+= -DCONFIG_FILO +endif + +# 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=7 -DCONGESTED + +# More optional features +# CFLAGS+= -DTRY_FLOPPY_FIRST=4 +# CFLAGS+= -DEXIT_IF_NO_OFFER + +# For a serial console, which can run in parallel with FIRMWARE console +# CFLAGS+= -DCONSOLE_DUAL -DCOMCONSOLE=0x3F8 -DCONSPEED=9600 + +# Enable tagged image, generic ELF, Multiboot ELF +# or FreeBSD ELF/a.out boot image support +CFLAGS+= -DTAGGED_IMAGE -DELF_IMAGE +# CFLAGS+= -DAOUT_IMAGE -DIMAGE_MULTIBOOT -DIMAGE_FREEBSD +# CFLAGS+= -DAOUT_IMAGE -DAOUT_LYNX_KDI +# CFLAGS+= -DCOFF_IMAGE +# CFLAGS+= -DRAW_IMAGE + +# Download files via TFTP +CFLAGS+= -DDOWNLOAD_PROTO_TFTP +# Change download protocol to NFS, default is TFTP +# CFLAGS+= -DDOWNLOAD_PROTO_NFS +# Change download protocol to HTTP, default is TFTP +# CFLAGS+= -DDOWNLOAD_PROTO_HTTP +# Change default protocol to NFS +# CFLAGS+= -DDEFAULT_PROTO_NFS +# Support to resolve hostnames in boot filename +# CFLAGS+= -DDNS_RESOLVER + +# Multicast Support +# CFLAGS+= -DALLMULTI -DMULTICAST_LEVEL1 -DMULTICAST_LEVEL2 -DDOWNLOAD_PROTO_TFTM + +# 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 + +# Include an auto-incrementing build serial number and optional build +# ID string +# CFLAGS+= -DBUILD_SERIAL +# CFLAGS+= -DBUILD_SERIAL -DBUILD_ID=\"testing\" + +# Do not relocate +# core/relocate.c should really be moved to an arch specific directory +# but this is here for archs that don't support relocation +# CFLAGS+= -DNORELOCATE + +# you should normally not need to change these +HOST_CC= gcc +CPP= gcc -E -Wp,-Wall +RM= rm -f +TOUCH= touch +PERL= /usr/bin/perl +CC= gcc +AS= as +LD= ld +SIZE= size +AR= ar +RANLIB= ranlib +OBJCOPY= objcopy + +CFLAGS+= -Os -ffreestanding +CFLAGS+= -Wall -W -Wno-format +CFLAGS+= $(EXTRA_CFLAGS) +ASFLAGS+= $(EXTRA_ASFLAGS) +LDFLAGS+= $(EXTRA_LDFLAGS) +# For debugging +# LDFLAGS+= -Map $@.map + +# Location to place generated binaries, and files +BIN=bin diff --git a/src/Families b/src/Families new file mode 100644 index 00000000..8a2af713 --- /dev/null +++ b/src/Families @@ -0,0 +1,133 @@ +# This is the config file for creating Makefile rules for Etherboot ROMs +# +# To make a ROM for a supported NIC locate the appropriate family +# and add a line of the form +# +# ROM PCI-IDs Comment +# +# ROM is the desired output name for both .rom and .lzrom images. +# PCI IDs are the PCI vendor and device IDs of the PCI NIC +# For ISA NICs put - +# +# All PCI ROMs that share a single driver are only built once (because they +# only have different PCI-IDs, but identical code). ISA ROMS are built for +# each ROM type, because different vendors used a different logic around the +# basic chip. The most popular example is the NS8390, which some cards use +# in PIO mode, some in DMA mode. Two chips currently don't fit into this nice +# black-and-white scheme (the Lance and the NS8390). Their driver deals +# with both PCI and ISA cards. These drivers will be treated similarly to +# ISA only drivers by genrules.pl and are compiled for each ROM type that is +# ISA, and additionally compiled for the PCI card type. +# +# Then do: make clean, make Roms and make +# +# Please send additions to this file to + +# Start of configuration + +family drivers/net/skel + +family arch/ia64/drivers/net/undi_nii +undi_nii - + +# 3c59x cards (Vortex) and 3c900 cards +# If your 3c900 NIC detects but fails to work, e.g. no link light, with +# the 3c90x driver, try using the 3c595 driver. I have one report that the +# 3c595 driver handles these NICs properly. (The 595 driver uses the +# programmed I/O mode of operation, whereas the 90x driver uses the bus +# mastering mode. These NICs are capable of either mode.) When it comes to +# making a ROM, as usual, you must choose the correct image, the one that +# contains the same PCI IDs as your NIC. +family drivers/net/3c595 + +# 3Com 3c90x cards +family drivers/net/3c90x + +# Intel Etherexpress Pro/100 +family drivers/net/eepro100 + +#Intel Etherexpress Pro/1000 +family drivers/net/e1000 + +#Broadcom Tigon 3 +family drivers/net/tg3 + +family drivers/net/pcnet32 + +# National Semiconductor ns83820 (Gigabit) family +family drivers/net/ns83820 + +family drivers/net/tulip + +family drivers/net/davicom + +family drivers/net/rtl8139 + +family drivers/net/r8169 + +family drivers/net/via-rhine + +family drivers/net/w89c840 + +family drivers/net/sis900 + +family drivers/net/natsemi + +family drivers/net/prism2_plx + +family drivers/net/prism2_pci +# Various Prism2.5 (PCI) devices that manifest themselves as Harris Semiconductor devices +# (with the actual vendor appearing as the vendor of the first subsystem) +hwp01170 0x1260,0x3873 ActionTec HWP01170 +dwl520 0x1260,0x3873 DLink DWL-520 + +family drivers/net/ns8390 +wd - WD8003/8013, SMC8216/8416, SMC 83c790 (EtherEZ) +ne - NE1000/2000 and clones +3c503 - 3Com503, Etherlink II[/16] + +family drivers/net/epic100 + +family drivers/net/3c509 +3c509 - 3c509, ISA/EISA +3c529 - 3c529 == MCA 3c509 + +family drivers/net/3c515 +3c515 - 3c515, Fast EtherLink ISA + +family drivers/net/eepro +eepro - Intel Etherexpress Pro/10 + +family drivers/net/cs89x0 +cs89x0 - Crystal Semiconductor CS89x0 + +family drivers/net/depca +depca - Digital DE100 and DE200 + +family drivers/net/forcedeth + +family drivers/net/sk_g16 +sk_g16 - Schneider and Koch G16 + +family drivers/net/smc9000 +smc9000 - SMC9000 + +family drivers/net/sundance + +family drivers/net/tlan + +family drivers/disk/ide_disk +ide_disk 0x0000,0x0000 Generic IDE disk support + +family drivers/disk/pc_floppy + +family arch/i386/drivers/net/undi +undi 0x0000,0x0000 UNDI driver support + +family drivers/net/pnic + +family arch/armnommu/drivers/net/p2001_eth + +family drivers/net/mtd80x + +family drivers/net/dmfe diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 00000000..afb1618e --- /dev/null +++ b/src/Makefile @@ -0,0 +1,15 @@ +# Override ARCH here or on the command line +# ARCH=i386 +# Additionally you can supply extra compilation arguments, e.g. for x86_64 +# EXTRA_CFLAGS=-m32 +# EXTRA_ASFLAGS=--32 +# EXTRA_LDFLAGS=-m elf_i386 +ifndef ARCH +ARCH:=$(shell uname -m | sed -e s,i[3456789]86,i386,) +endif +MAKEDEPS:= +SUFFIXES:= + +include Config +include arch/$(ARCH)/Config +include Makefile.main diff --git a/src/Makefile-armnommu b/src/Makefile-armnommu new file mode 100644 index 00000000..04c4002b --- /dev/null +++ b/src/Makefile-armnommu @@ -0,0 +1,18 @@ +ARCH:=armnommu +MAKEDEPS:= + +include Config +include arch/$(ARCH)/Config + +CC= $(CROSS_COMPILE)gcc +AS= $(CROSS_COMPILE)as +LD= $(CROSS_COMPILE)ld +SIZE= $(CROSS_COMPILE)size +AR= $(CROSS_COMPILE)ar +RANLIB= $(CROSS_COMPILE)ranlib +OBJCOPY= $(CROSS_COMPILE)objcopy + +MAKEDEPS+=Makefile-armnommu +BIN=bin + +include Makefile.main diff --git a/src/Makefile-e1 b/src/Makefile-e1 new file mode 100644 index 00000000..bdd4917a --- /dev/null +++ b/src/Makefile-e1 @@ -0,0 +1,18 @@ +ARCH:=e1 +MAKEDEPS:= + +include arch/$(ARCH)/Config +include Config + +CC= e1-coff-gcc +AS= e1-coff-as +LD= e1-coff-ld +SIZE= e1-coff-size +AR= e1-coff-ar +RANLIB= e1-coff-ranlib +OBJCOPY=e1-coff-objcopy + +MAKEDEPS+=Makefile-e1 +BIN=bin-e1 + +include Makefile.main diff --git a/src/Makefile-i386 b/src/Makefile-i386 new file mode 100644 index 00000000..d6268520 --- /dev/null +++ b/src/Makefile-i386 @@ -0,0 +1,18 @@ +ARCH:=i386 +MAKEDEPS:= + +include arch/$(ARCH)/Config +include Config + +CC= i386-linux-gcc +AS= i386-linux-as +LD= i386-linux-ld +SIZE= i386-linux-size +AR= i386-linux-ar +RANLIB= i386-linux-ranlib +OBJCOPY= i386-linux-objcopy + +MAKEDEPS+=Makefile-i386 +BIN=bin-i386 + +include Makefile.main diff --git a/src/Makefile-ia64 b/src/Makefile-ia64 new file mode 100644 index 00000000..503f7e03 --- /dev/null +++ b/src/Makefile-ia64 @@ -0,0 +1,18 @@ +ARCH:=ia64 +MAKEDEPS:= + +include arch/$(ARCH)/Config +include Config + +CC= ia64-linux-gcc +AS= ia64-linux-as +LD= ia64-linux-ld +SIZE= ia64-linux-size +AR= ia64-linux-ar +RANLIB= ia64-linux-ranlib +OBJCOPY= ia64-linux-objcopy + +MAKEDEPS+=Makefile-ia64 +BIN=bin-ia64 + +include Makefile.main diff --git a/src/Makefile.main b/src/Makefile.main new file mode 100644 index 00000000..2108db16 --- /dev/null +++ b/src/Makefile.main @@ -0,0 +1,450 @@ +# +# Makefile for Etherboot +# +# Most of the time you should edit Config +# +# Common options: +# VERSION_*=v - Set the major and minor version numbers +# +# 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 +# +# 3C509 option: +# -DINCLUDE_3C509 - Include 3c509 support +# +# 3C90X options: +# -DINCLUDE_3C90X - Include 3c90x support +# +# 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: +# -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 +# +# RTL8139 options: +# -DINCLUDE_RTL8139 - Include RTL8139 support +# +# SIS900 options: +# -DINCLUDE_SIS900 - Include SIS900 support +# +# NATSEMI options: +# -DINCLUDE_NATSEMI - Include NATSEMI support +# + +SRCS:= +BOBJS:= + +MAKEROM= $(PERL) ./util/makerom.pl +VERSION_MAJOR= 5 +VERSION_MINOR= 3 +VERSION_PATCH= 14 +EXTRAVERSION= +VERSION= $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH)$(EXTRAVERSION) +MM_VERSION= $(VERSION_MAJOR).$(VERSION_MINOR) +CFLAGS+= -DVERSION_MAJOR=$(VERSION_MAJOR) \ + -DVERSION_MINOR=$(VERSION_MINOR) \ + -DVERSION=\"$(VERSION)\" $(OLDGAS) \ + -I include -I arch/$(ARCH)/include \ + -DARCH=$(ARCH) +FILO=filo +FILO_PROGRAM_NAME = FILO +FILO_PROGRAM_VERSION = 0.4.1 +FILO_BUILD_INFO = ($(shell whoami)@$(shell hostname)) $(shell LANG=C date) + +GCCINCDIR = $(shell $(CC) -print-search-dirs | head -n 1 | cut -d' ' -f2)include +CPPFLAGS = -nostdinc -imacros filo/config.h +#-Ifilo/include -I$(GCCINCDIR) -MD +ASFLAGS_X = -D__ASSEMBLY__ + +IDENT= '$(@F) $(VERSION) (GPL) etherboot.org' + +# 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) + +BUILD_LIBS= $(BLIB) +BUILD_IMGS= $(IMGS) + +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 +E1000FLAGS= -DINCLUDE_E1000 +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 +SIS900FLAGS= -DINCLUDE_SIS900 +NATSEMIFLAGS= -DINCLUDE_NATSEMI +SMC9000FLAGS= -DINCLUDE_SMC9000 +SUNDANCEFLAGS= -DINCLUDE_SUNDANCE +TLANFLAGS= -DINCLUDE_TLAN +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 + +SRCS+= core/serial.c + +SRCS+= core/btext.c core/pc_kbd.c + +SRCS+= core/main.c core/pci.c core/osloader.c core/nfs.c +SRCS+= core/misc.c core/config.c core/isa_probe.c core/pci_probe.c +SRCS+= core/relocate.c core/heap.c +SRCS+= drivers/disk/floppy.c core/nic.c core/disk.c core/timer.c +SRCS+= core/proto_eth_slow.c +SRCS+= core/proto_slam.c core/proto_tftm.c core/proto_http.c +SRCS+= core/isapnp.c +SRCS+= core/pcmcia.c core/i82365.c +SRCS+= core/pxe_export.c core/dns_resolver.c + +FILO_SRCS+= $(FILO)/drivers/ide_x.c +FILO_SRCS+= $(FILO)/fs/blockdev.c $(FILO)/fs/eltorito.c $(FILO)/fs/fsys_ext2fs.c $(FILO)/fs/fsys_fat.c $(FILO)/fs/fsys_iso9660.c +FILO_SRCS+= $(FILO)/fs/fsys_reiserfs.c $(FILO)/fs/vfs.c $(FILO)/fs/fsys_jfs.c $(FILO)/fs/fsys_minix.c $(FILO)/fs/fsys_xfs.c +FILO_SRCS+= $(FILO)/main/elfload.c $(FILO)/main/elfnote.c $(FILO)/main/filo_x.c $(FILO)/main/lib.c $(FILO)/main/linuxbios_x.c +FILO_SRCS+= $(FILO)/main/pci_x.c $(FILO)/main/malloc_x.c $(FILO)/main/printf_x.c $(FILO)/main/console_x.c +FILO_SRCS+= $(FILO)/$(ARCH)/context.c $(FILO)/$(ARCH)/linux_load.c $(FILO)/$(ARCH)/segment.c $(FILO)/$(ARCH)/sys_info.c +FILO_SRCS+= $(FILO)/$(ARCH)/switch.S $(FILO)/usb/debug_x.c $(FILO)/usb/scsi_cmds.c $(FILO)/usb/uhci.c $(FILO)/usb/usb.c +FILO_SRCS+= $(FILO)/usb/ohci.c $(FILO)/usb/usb_scsi_low.c $(FILO)/usb/usb_x.c + + +BOBJS+= $(BIN)/main.o $(BIN)/osloader.o $(BIN)/nfs.o $(BIN)/misc.o +BOBJS+= $(BIN)/proto_slam.o $(BIN)/proto_tftm.o $(BIN)/proto_http.o +BOBJS+= $(BIN)/floppy.o +BOBJS+= $(BIN)/serial.o $(BIN)/timer.o $(BIN)/relocate.o $(BIN)/heap.o +BOBJS+= $(BIN)/btext.o $(BIN)/pc_kbd.o +BOBJS+= $(BIN)/nic.o $(BIN)/disk.o +BOBJS+= $(BIN)/isapnp.o +BOBJS+= $(BIN)/pci.o $(BIN)/isa_probe.o $(BIN)/pci_probe.o +BOBJS+= $(BIN)/vsprintf.o $(BIN)/string.o +BOBJS+= $(BIN)/pcmcia.o $(BIN)/i82365.o +BOBJS+= $(BIN)/pxe_export.o $(BIN)/dns_resolver.o + +FILO_OBJS+= $(BIN)/ide_x.o $(BIN)/pci_x.o +FILO_OBJS+= $(BIN)/blockdev.o $(BIN)/eltorito.o $(BIN)/fsys_ext2fs.o $(BIN)/fsys_fat.o $(BIN)/fsys_iso9660.o $(BIN)/fsys_reiserfs.o $(BIN)/vfs.o +FILO_OBJS+= $(BIN)/fsys_jfs.o $(BIN)/fsys_minix.o $(BIN)/fsys_xfs.o +FILO_OBJS+= $(BIN)/elfload.o $(BIN)/elfnote.o $(BIN)/filo_x.o $(BIN)/lib.o $(BIN)/linuxbios_x.o $(BIN)/malloc_x.o $(BIN)/printf_x.o $(BIN)/console_x.o +FILO_OBJS+= $(BIN)/context.o $(BIN)/linux_load.o $(BIN)/segment.o $(BIN)/sys_info.o $(BIN)/switch.o +FILO_OBJS+= $(BIN)/debug_x.o $(BIN)/scsi_cmds.o $(BIN)/uhci.o $(BIN)/usb.o $(BIN)/ohci.o $(BIN)/usb_scsi_low.o $(BIN)/usb_x.o + +BLIB= $(BIN)/bootlib.a +FILOLIB= $(BIN)/filolib.a +LIBS= $(BLIB) +ifdef INCLUDE_FILO +LIBS+= $(FILOLIB) +endif +UTILS+= $(BIN)/nrv2b +STDDEPS= $(START) $(UTILS) +# MAKEDEPS is the one target that is depended by all ROMs, so we check gcc here +# If you are confident that gcc 2.96 works for you, you can remove the lines +# that check gcc in the toolcheck rule +MAKEDEPS+= Makefile Makefile.main Config genrules.pl Families +MAKEDEPS+= $(BIN)/toolcheck +MAKEDEPS+= arch/$(ARCH)/Makefile arch/$(ARCH)/Config + +# Start of targets + +.PHONY: noargs +noargs: $(BIN)/toolcheck + @echo '====================================================' + @echo 'No target specified. To specify a target, do: ' + @echo + @echo ' $(MAKE) bin/. ' + @echo + @echo 'where is one of {zdsk, zrom, iso, liso, zlilo, zpxe, elf, com}' + @echo + @echo 'or: ' + @echo + @echo ' $(MAKE) alls' + @echo + @echo 'to generate all possible images of format ' + @echo + @echo 'For example, ' + @echo + @echo ' make allzroms ' + @echo + @echo 'will generate all possible .zrom (rom burnable) images, and' + @echo + @echo ' make allzdsks' + @echo + @echo 'will generate all possible .zdsk (bootable floppy) images, or' + @echo + @echo '====================================================' + @exit 1 + +$(BIN)/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; \ + else \ + touch $(BIN)/toolcheck; \ + fi; \ + if [ `perl -e 'use bytes; print chr(255)' | wc -c` = 2 ]; \ + then \ + echo 'Your Perl version has a Unicode handling bug'; \ + echo 'To workaround, execute this before compiling Etherboot:'; \ + echo 'export LANG=$${LANG%.UTF-8}'; \ + exit 1; \ + fi + +include arch/$(ARCH)/Makefile + +# Common files + +$(BLIB): $(BOBJS) + $(AR) r $@ $(BOBJS) + $(RANLIB) $@ + +$(FILOLIB): $(FILO_OBJS) + $(AR) r $@ $(FILO_OBJS) + $(RANLIB) $@ + +# LinuxBIOS support code +$(BIN)/linuxbios.o: firmware/linuxbios/linuxbios.c include/etherboot.h include/dev.h firmware/linuxbios/linuxbios_tables.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. + +$(FILO)/config.h: $(FILO)/Config + /bin/echo -e '/* GENERATED FILE, DO NOT EDIT */\n' >$@ + sed -e 's/#.*//' -e '/=/!d' -e 's/\([^[:space:]]*\)[[:space:]]*=[[:space:]]*\(.*\).*/#define \1 \2/' -e 's/^#define \([^ ]*\) 0$$/#undef \1/' $^ >>$@ + +filo_version: $(FILO)/main/version.h + +$(FILO)/main/version.h: FORCE + echo '#define PROGRAM_NAME "$(FILO_PROGRAM_NAME)"' > $@ + echo '#define PROGRAM_VERSION "$(FILO_PROGRAM_VERSION) $(FILO_BUILD_INFO)"' >> $@ + +FORCE: + + +# Utilities + +$(BIN)/lzhuf: util/lzhuf.c + $(HOST_CC) -O2 -DENCODE -DDECODE -DMAIN -DVERBOSE -o $@ $< + +# Roms file +# Builds almost silently because this rule is triggered for just about +# every modification to sources. + +$(BIN)/Roms $(BIN)/NIC: genrules.pl Families $(SRCS) + @mkdir -p $(@D) + @echo Scanning for ROMs and dependencies... + @$(PERL) ./genrules.pl Families $(BIN)/NIC $(ARCH) $(SRCS) > $(BIN)/Roms + +# Pattern Rules + +# general rules for compiling/assembling source files +$(BIN)/%.o: core/%.c $(MAKEDEPS) + $(CC) $(CFLAGS) -o $@ -c $< + +$(BIN)/%.s: core/%.c $(MAKEDEPS) + $(CC) $(CFLAGS) -S -o $@ -c $< + +$(BIN)/%.o: drivers/disk/%.c $(MAKEDEPS) + $(CC) $(CFLAGS) -o $@ -c $< + +$(BIN)/%.o: drivers/net/%.c $(MAKEDEPS) + $(CC) $(CFLAGS) -o $@ -c $< + +$(BIN)/%.o: firmware/linuxbios/%.c $(MAKEDEPS) + $(CC) $(CFLAGS) -o $@ -c $< + +$(BIN)/%.o: $(FILO)/drivers/%.c $(MAKEDEPS) $(FILO)/config.h + $(CC) $(CFLAGS) -imacros $(FILO)/config.h -o $@ -c $< + +$(BIN)/%.o: $(FILO)/fs/%.c $(MAKEDEPS) $(FILO)/config.h + $(CC) $(CFLAGS) -imacros $(FILO)/config.h -o $@ -c $< + +$(BIN)/%.o: $(FILO)/$(ARCH)/%.c $(MAKEDEPS) $(FILO)/config.h + $(CC) $(CFLAGS) -imacros $(FILO)/config.h -o $@ -c $< + +$(BIN)/%.o: $(FILO)/$(ARCH)/%.S $(MAKEDEPS) $(FILO)/config.h + $(CC) $(ASFLAGS_X) $(CPPFLAGS) -c $< -o $@ + +$(BIN)/%.o: $(FILO)/main/%.c $(MAKEDEPS) $(FILO)/config.h filo_version + $(CC) $(CFLAGS) -imacros $(FILO)/config.h -o $@ -c $< + +$(BIN)/%.o: $(FILO)/usb/%.c $(MAKEDEPS) $(FILO)/config.h + $(CC) $(CFLAGS) -imacros $(FILO)/config.h -o $@ -c $< + +# Rule for the super etherboot image. +$(BIN)/etherboot.o: $(DOBJS) + $(LD) $(LDFLAGS) -r $(DOBJS) -o $@ + +$(BIN)/etherboot-pci.o: $(PCIOBJS) + $(LD) $(LDFLAGS) -r $(PCIOBJS) -o $@ + +# General rules for generating runtime (rt) files +$(BIN)/%.rt.o: $(BIN)/%.o $(START) $(BIN)/config.o $(LIBS) $(STDDEPS) $(MAKEDEPS) + $(LD) $(LDFLAGS) -r $(START) $(BIN)/config.o $< $(LIBS) -o $@ + +# Rule for $(BIN)/%.FORMAT.rt is architecture and target-format specific + +$(BIN)/%.rt.bin: $(BIN)/%.rt $(MAKEDEPS) + $(OBJCOPY) -O binary -R .prefix $< $@ + +$(BIN)/%.rt1.bin: $(BIN)/%.rt $(MAKEDEPS) + $(OBJCOPY) -O binary -j .text.nocompress $< $@ + +$(BIN)/%.rt2.bin: $(BIN)/%.rt $(MAKEDEPS) + $(OBJCOPY) -O binary -R .prefix -R .text.nocompress $< $@ + +# Rules for generating prefix binary files + +# Rule for $(BIN)/%.FORMAT.prf is architecture and target-format specific +$(BIN)/%.prf.bin: $(BIN)/%.prf $(MAKEDEPS) + $(OBJCOPY) -j .prefix -O binary $< $@ + +# general rule for .z (compressed binary code), may be overridden +$(BIN)/%.zbin: $(BIN)/%.bin $(BIN)/nrv2b $(MAKEDEPS) + $(BIN)/nrv2b e $< $@ + +# Housekeeping + +clean: + $(RM) $(BIN)/* + $(RM) $(FILO)/config.h $(FILO)/main/version.h + +../index.html: ../index.xhtml + (cd ..; m4 -P -DHOSTSITE=SOURCEFORGE index.xhtml > index.html) + +../index-berlios.html: ../index.xhtml + (cd ..; m4 -P -DHOSTSITE=BERLIOS index.xhtml > index-berlios.html) + +tarball: ../index.html ../index-berlios.html + (echo -n $(VERSION) ''; date -u +'%Y-%m-%d') > ../VERSION + (cd ../..; tar cf /tmp/etherboot-$(VERSION).tar --exclude CVS --exclude doc etherboot-$(VERSION)) + bzip2 -9 < /tmp/etherboot-$(VERSION).tar > /tmp/etherboot-$(VERSION).tar.bz2 + gzip -9 < /tmp/etherboot-$(VERSION).tar > /tmp/etherboot-$(VERSION).tar.gz + +# Auto-incrementing build serial number. Is auto-incremented for each +# make run that specifies a final image file (e.g. bin/undi.zpxe) as a +# target, or a target of the form "all*". Enable via -DBUILD_SERIAL +# in Config. + +ifneq ($(findstring -DBUILD_SERIAL,$(CFLAGS)),) + +# If no make goals are specified, it means "make all" +REALGOALS = $(if $(MAKECMDGOALS),$(MAKECMDGOALS),all) + +# Filter to see if there are any targets to trigger an auto-increment +BUILDGOALS = $(filter all,$(REALGOALS)) $(filter all%,$(REALGOALS)) \ + $(foreach SUFFIX,$(SUFFIXES),$(filter %.$(SUFFIX),$(REALGOALS))) + +ifneq ($(strip $(BUILDGOALS)),) +# This is an auto-incrementing build. Forcibly rebuild .buildserial.h +# and mark config.o as depending on it to force its rebuilding. +bin/config.o : include/.buildserial.h +.PHONY : include/.buildserial.h +endif # BUILDGOALS + +include/.buildserial.h : + @if [ ! -f $@ ]; then echo '#define BUILD_SERIAL_NUM 0' > $@; fi + @perl -pi -e 's/(BUILD_SERIAL_NUM)\s+(\d+)/"$${1} ".($${2}+1)/e' $@ + +buildserial : include/.buildserial.h + @perl -n -e '/BUILD_SERIAL_NUM\s+(\d+)/ && ' \ + -e 'print "Build serial number is $$1\n";' $< + +else # -DBUILD_SERIAL + +buildserial : + @echo Build serial number is disabled. Enable -DBUILD_SERIAL in Config. + +endif # -DBUILD_SERIAL + +bs : buildserial + +version: + @echo $(VERSION) + +romlimit: + @echo $(ROMLIMIT) + +sources: + @echo $(SRCS) diff --git a/src/README.pixify b/src/README.pixify new file mode 100644 index 00000000..9aef25d9 --- /dev/null +++ b/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/src/arch/armnommu/Config b/src/arch/armnommu/Config new file mode 100644 index 00000000..4c220cd5 --- /dev/null +++ b/src/arch/armnommu/Config @@ -0,0 +1,23 @@ +# Config for armnommu Etherboot +# + +# For a clean compilation, switch in global Config +# off: -DCONFIG_PCI, -DTAGGED_IMAGE, -DELF_IMAGE, -DPXE*, -DRELOCATE and INCLUDE_FILO +# on : -DRAW_IMAGE + +# Serial line settings +CFLAGS+= -DCONSOLE_SERIAL -DCONSPEED=57600 + +# System Frequency +CFLAGS+= -DSYSCLK=73728000 + +# Image Download Address +CFLAGS+= -DRAWADDR=0x40100000 + +# NIC Debug Outputs +#CFLAGS+= -DDEBUG_NIC + +# Fixed MAC address +# p2001_eth has no flash and fixed mac address +#CFLAGS+= -DMAC_HW_ADDR_DRV="'H','Y','L','N','X','1'" +CFLAGS+= -DMAC_HW_ADDR_DRV="0x00,0x09,0x4F,0x00,0x00,0x02" diff --git a/src/arch/armnommu/Makefile b/src/arch/armnommu/Makefile new file mode 100644 index 00000000..a0cbb0c0 --- /dev/null +++ b/src/arch/armnommu/Makefile @@ -0,0 +1,58 @@ +ARCH_FORMAT= armnommu + +ROMLIMIT= 20480 +CHECKSIZE= { read d1; read d1 d2 d3 size d4; [ $$size -gt $(ROMLIMIT) ] &&\ + { $(RM) $@; echo "ERROR: code size exceeds limit!"; exit 1; }; exit 0; } + +START= $(BIN)/start.o + +SRCS+= arch/armnommu/core/arm_timer.c +SRCS+= arch/armnommu/core/start.S +SRCS+= arch/armnommu/core/serial.c +SRCS+= arch/armnommu/core/mem.c +SRCS+= arch/armnommu/core/setjmp.S +SRCS+= arch/armnommu/drivers/net/p2001_eth.c + +# not greater than 100kB +ROMLIMIT:=1024000 + +include $(BIN)/Roms + +ROMS= $(BIN)/p2001_eth.rom +IMGS= $(BIN)/p2001_eth.img + + +allfiles: $(ROMS) + +BOBJS+= $(BIN)/arm_timer.o +BOBJS+= $(BIN)/serial.o +BOBJS+= $(BIN)/mem.o +BOBJS+= $(BIN)/setjmp.o +BOBJS+= $(BIN)/lib1funcs.o + +# Utilities + +$(BIN)/nrv2b: util/nrv2b.c + $(HOST_CC) -O2 -DENCODE -DDECODE -DMAIN -DVERBOSE -DNDEBUG -DBITSIZE=32 -DENDIAN=0 -o $@ $< + +# Pattern Rules +# General for compiling/assembly source files +$(BIN)/%.o: arch/armnommu/core/%.c $(MAKEDEPS) + $(CC) $(CFLAGS) -o $@ -c $< + +$(BIN)/%.o: arch/armnommu/drivers/net/%.c $(MAKEDEPS) + $(CC) $(CFLAGS) -o $@ -c $< + +$(BIN)/%.S: arch/armnommu/core/%.c $(MAKEDEPS) + $(CC) $(CFLAGS) -S -o $@ -c $< + +$(BIN)/%.o: arch/armnommu/core/%.S $(MAKEDEPS) + $(CPP) $(CFLAGS) -D ASSEMBLY $< | $(AS) $(ASFLAGS) -o $@ + +# general ruls for generating .img files +$(BIN)/%.tmp: $(BIN)/%.o $(START) $(BIN)/config.o arch/$(ARCH)/core/etherboot.lds $(LIBS) $(STDDEPS) $(MAKEDEPS) + $(LD) $(LDFLAGS) -T arch/$(ARCH)/core/etherboot.lds -o $@ $(START) $(BIN)/config.o $< $(LIBS) + @$(SIZE) $@ | $(CHECKSIZE) + +$(BIN)/%.img: $(BIN)/%.tmp $(MAKEDEPS) + $(OBJCOPY) -O binary $< $@ diff --git a/src/arch/armnommu/core/arm_timer.c b/src/arch/armnommu/core/arm_timer.c new file mode 100644 index 00000000..b858ca8d --- /dev/null +++ b/src/arch/armnommu/core/arm_timer.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * 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. + */ +#include "etherboot.h" +#include "timer.h" +#include "latch.h" +#include "hardware.h" + + +/* get timer returns the contents of the timer */ +static unsigned long get_timer(void) +{ + return P2001_TIMER->Freerun_Timer; +} + +/* ------ Calibrate the TSC ------- + * Time how long it takes to excute a loop that runs in known time. + * And find the convertion needed to get to CLOCK_TICK_RATE + */ + +static unsigned long configure_timer(void) +{ + return (1); +} + +static unsigned long clocks_per_tick = 1; + +void setup_timers(void) +{ + if (!clocks_per_tick) { + clocks_per_tick = configure_timer(); + } +} + +unsigned long currticks(void) +{ + return get_timer(); /* /clocks_per_tick */ +} + +static unsigned long timer_timeout; +static int __timer_running(void) +{ + return get_timer() < timer_timeout; +} + +void udelay(unsigned int usecs) +{ + unsigned long now; + now = get_timer(); + timer_timeout = now + usecs * ((clocks_per_tick * TICKS_PER_SEC)/(1000*1000)); + while(__timer_running()); +} +void ndelay(unsigned int nsecs) +{ + unsigned long now; + now = get_timer(); + timer_timeout = now + nsecs * ((clocks_per_tick * TICKS_PER_SEC)/(1000*1000*1000)); + while(__timer_running()); +} + +void load_timer2(unsigned int timer2_ticks) +{ + unsigned long now; + unsigned long clocks; + now = get_timer(); + clocks = timer2_ticks * ((clocks_per_tick * TICKS_PER_SEC)/CLOCK_TICK_RATE); + timer_timeout = now + clocks; +} + +int timer2_running(void) +{ + return __timer_running(); +} diff --git a/src/arch/armnommu/core/etherboot.lds b/src/arch/armnommu/core/etherboot.lds new file mode 100644 index 00000000..0901a2f9 --- /dev/null +++ b/src/arch/armnommu/core/etherboot.lds @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * 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. + */ +OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") +OUTPUT_ARCH(arm) +ENTRY(_start) +SECTIONS +{ + /*. = 0x00000000;*/ /* PIC */ + /*. = 0x00000400;*/ /* ROM Bootloader */ + . = 0x40000000; /* SDRAM */ + + . = ALIGN(4); + _text = . ; + .text : + { + _start = .; + _virt_start = .; + bin/start.o (.text) + *(.text) + + . = ALIGN(16); + isa_drivers = . ; + *(.drivers.isa); + isa_drivers_end = . ; + } + + . = ALIGN(4); + .rodata : { *(.rodata) } + + . = ALIGN(4); + .data : { *(.data) } + + . = ALIGN(4); + .got : { *(.got) } + + . = ALIGN(4); + _bss = . ; + .bss : { *(.bss) } + + . = ALIGN(4); + _ebss = .; + _end = .; + + . = ALIGN(16); + .text : + { + *(.dma.desc); + *(.dma.buffer); + } +} diff --git a/src/arch/armnommu/core/lib1funcs.c b/src/arch/armnommu/core/lib1funcs.c new file mode 100644 index 00000000..e9e3a8fa --- /dev/null +++ b/src/arch/armnommu/core/lib1funcs.c @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * 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. + */ + + +/* + * from gcc/config/udivmodsi4.c + */ +unsigned long +udivmodsi4(unsigned long num, unsigned long den, int modwanted) +{ + unsigned long bit = 1; + unsigned long res = 0; + + while (den < num && bit && !(den & (1L<<31))) + { + den <<=1; + bit <<=1; + } + while (bit) + { + if (num >= den) + { + num -= den; + res |= bit; + } + bit >>=1; + den >>=1; + } + if (modwanted) return num; + return res; +} + + +/* + * from gcc/config/udivmod.c + */ +long +__udivsi3 (long a, long b) +{ + return udivmodsi4 (a, b, 0); +} + +long +__umodsi3 (long a, long b) +{ + return udivmodsi4 (a, b, 1); +} + + +/* + * from gcc/config/divmod.c + */ +long +__divsi3 (long a, long b) +{ + int neg = 0; + long res; + + if (a < 0) + { + a = -a; + neg = !neg; + } + + if (b < 0) + { + b = -b; + neg = !neg; + } + + res = udivmodsi4 (a, b, 0); + + if (neg) + res = -res; + + return res; +} + +long +__modsi3 (long a, long b) +{ + int neg = 0; + long res; + + if (a < 0) + { + a = -a; + neg = 1; + } + + if (b < 0) + b = -b; + + res = udivmodsi4 (a, b, 1); + + if (neg) + res = -res; + + return res; +} diff --git a/src/arch/armnommu/core/mem.c b/src/arch/armnommu/core/mem.c new file mode 100644 index 00000000..a185c5d6 --- /dev/null +++ b/src/arch/armnommu/core/mem.c @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * 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. + */ +#include "hooks.h" +#include "io.h" +#include "etherboot.h" + +struct meminfo meminfo; +void get_memsizes(void) +{ +/* We initialize the meminfo structure + * according to our development board's specs + * We do not have a way to automatically probe the + * memspace instead we initialize it manually + */ + meminfo.basememsize = 0x00000000; + meminfo.memsize = 0x00008000; + meminfo.map_count = 1; + + meminfo.map[0].addr = 0x40000000; + meminfo.map[0].size = 0x01000000; + meminfo.map[0].type = E820_RAM; +} diff --git a/src/arch/armnommu/core/raw_loader.c b/src/arch/armnommu/core/raw_loader.c new file mode 100644 index 00000000..1d49ffeb --- /dev/null +++ b/src/arch/armnommu/core/raw_loader.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * 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. + */ +#ifdef RAW_IMAGE +static unsigned long raw_load_addr; + +int mach_boot(register unsigned long entry_point) +{ + void (*fnc)(void) = (void *) entry_point; + // r0 = 0 + // r1 = 625 (machine nr. MACH_TYPE_P2001) + (*fnc)(); + + return 0; /* We should never reach this point ! */ +} + +static sector_t raw_download(unsigned char *data, unsigned int len, int eof) +{ + memcpy(phys_to_virt(raw_load_addr), data, len); + raw_load_addr += len; + if (!eof) + return 0; + + done(1); + printf("Starting program.\n"); + mach_boot(RAWADDR); + printf("Bootsector returned?"); + longjmp(restart_etherboot, -2); + return 1; +} + +static os_download_t raw_probe(unsigned char *data __unused, unsigned int len __unused) +{ + printf("(RAW"); + // probe something here... + printf(")... \n"); + + //raw_load_addr = phys_to_virt(_end); + raw_load_addr = RAWADDR; + printf("Writing image to 0x%x\n", raw_load_addr); + return raw_download; +} + +#endif diff --git a/src/arch/armnommu/core/serial.c b/src/arch/armnommu/core/serial.c new file mode 100644 index 00000000..3ae98d47 --- /dev/null +++ b/src/arch/armnommu/core/serial.c @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * 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. + */ +#include "etherboot.h" +#include "hardware.h" +#ifdef CONSOLE_SERIAL + +/* + * void serial_putc(int ch); + * Write character `ch' to port UART_BASE. + */ +void serial_putc(int ch) +{ + /* wait for room in the 32 byte tx FIFO */ + while ((P2001_UART->r.STATUS & 0x3f) > /* 30 */ 0) ; + P2001_UART->w.TX1 = ch & 0xff; +} + +/* + * int serial_getc(void); + * Read a character from port UART_BASE. + */ +int serial_getc(void) +{ + while (((P2001_UART->r.STATUS >> 6) & 0x3f) == 0) ; + return P2001_UART->r.RX1 & 0xff; +} + +/* + * int serial_ischar(void); + * If there is a character in the input buffer of port UART_BASE, + * return nonzero; otherwise return 0. + */ +int serial_ischar(void) +{ + return (P2001_UART->r.STATUS >> 6) & 0x3f; +} + +/* + * int serial_init(void); + * Initialize port to speed 57.600, line settings 8N1. + */ +int serial_init(void) +{ + static unsigned int N; + // const M=3 + P2001_UART->w.Clear = 0; // clear + N = ((SYSCLK/8)*3)/CONSPEED; + P2001_UART->w.Baudrate = (N<<16)+3; // set 57.600 BAUD + P2001_UART->w.Config = 0xcc100; // set 8N1, *water = 12 + return 1; +} + +/* + * void serial_fini(void); + * Cleanup our use of the serial port, in particular flush the + * output buffer so we don't accidentially loose characters. + */ +void serial_fini(void) +{ +} +#endif diff --git a/src/arch/armnommu/core/setjmp.S b/src/arch/armnommu/core/setjmp.S new file mode 100644 index 00000000..7e0a3eec --- /dev/null +++ b/src/arch/armnommu/core/setjmp.S @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * 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. + */ +.text + +.global sigsetjmp; +.type sigsetjmp,%function +.align 4; +sigsetjmp: + /* Save registers */ + stmia r0, {v1-v6, sl, fp, sp, lr} + + mov r0, #0 + bx lr +.size sigsetjmp,.-sigsetjmp; + + + +.global longjmp; +.type longjmp,%function +.align 4; +longjmp: + mov ip, r0 /* save jmp_buf pointer */ + + movs r0, r1 /* get the return value in place */ + moveq r0, #1 /* can't let setjmp() return zero! */ + + ldmia ip, {v1-v6, sl, fp, sp, pc} +.size longjmp,.-longjmp; diff --git a/src/arch/armnommu/core/start.S b/src/arch/armnommu/core/start.S new file mode 100644 index 00000000..61b1d31b --- /dev/null +++ b/src/arch/armnommu/core/start.S @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * 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. + */ +.global _start + +/* Mode definitions */ +#define Mode_USR 0x10 +#define Mode_FIQ 0x11 +#define Mode_IRQ 0x12 +#define Mode_SVC 0x13 +#define Mode_ABT 0x17 +#define Mode_UNDEF 0x1B +#define Mode_SYS 0x1F // only available on ARM Arch. v4 +#define I_Bit 0x80 +#define F_Bit 0x40 + +/* LPEC register definitions */ +#define Adr_SYS_BASE 0x00100000 +#define REL_Adr_SDRAM_Ctrl 0x10 +#define REL_Adr_ExtMem_Ctrl 0x14 +#define REL_Adr_WaitState_Ext 0x18 +#define REL_Adr_WaitState_Asic 0x1c +#define Adr_TIMER_BASE 0x00110000 +#define REL_Adr_Timer12_PreDiv 0x0c +#define REL_Adr_PLL_12000_config 0x30 +#define REL_Adr_PLL_12288_config 0x34 +#define REL_Adr_DIV_12288_config 0x38 +#define REL_Adr_FSC_CONFIG 0x44 +#define Adr_GPIO_BASE 0x00120000 +#define REL_Adr_NRES_OUT 0x2c + + + +/* Define entry point */ +.arm // Next instruction will be ARM +_start: + +/* + * Initialize memory system + */ + + +/* + * Initialize stack pointer registers + */ + +/* Enter SVC mode and set up the SVC stack pointer */ + mov r0, #(Mode_SVC|I_Bit|F_Bit) + msr cpsr_c, r0 + ldr sp, SP_SVC + + +/* + * Initialize critical IO devices + */ + + /* watchdog off */ + mov r0, #Adr_TIMER_BASE + ldr r1, Timer12_PreDiv + str r1, [r0, #REL_Adr_Timer12_PreDiv] + + /* NRES=1 */ + mov r0, #Adr_GPIO_BASE + ldr r1, NRES_OUT + str r1, [r0, #REL_Adr_NRES_OUT] + + /* ExtMem */ + mov r0, #Adr_SYS_BASE + ldr r1, ExtMem_Ctrl + str r1, [r0, #REL_Adr_ExtMem_Ctrl] + + /* SDRAM */ + mov r0, #Adr_SYS_BASE + ldr r1, SDRAM_Ctrl + str r1, [r0, #REL_Adr_SDRAM_Ctrl] +/* +_wait_sdram_ctrl: + ldr r1, [r0] + tst r1, #0x20000 + beq _wait_sdram_ctrl +*/ + + /* WaitState_Ext */ + ldr r1, WaitState_Ext + str r1, [r0, #REL_Adr_WaitState_Ext] + /* WaitState_Asic */ + ldr r1, WaitState_Asic + str r1, [r0, #REL_Adr_WaitState_Asic] + + /* PLL_12288 */ + mov r0, #Adr_TIMER_BASE + ldr r1, PLL_12288_config + str r1, [r0, #REL_Adr_PLL_12288_config] + /* DIV_12288 */ + ldr r1, DIV_12288_config + str r1, [r0, #REL_Adr_DIV_12288_config] + /* PLL_12200 */ + ldr r1, PLL_12000_config + str r1, [r0, #REL_Adr_PLL_12000_config] + + /* FSC_CONFIG */ + ldr r1, [r0, #REL_Adr_FSC_CONFIG] + bic r1, r1, #0x07 + ldr r2, FSC_CONFIG + orr r1, r1, r2 + str r1, [r0, #REL_Adr_FSC_CONFIG] + + +/* + * Initialize interrupt system variables here + */ + + +/* + * Initialize memory required by main C code + */ + + +/* jump to main program */ + mov r0, #0 + b main + + +Timer12_PreDiv: + .word 0x40bb0000 /* watchdog off */ +NRES_OUT: + .word 0x00000003 /* NRES_OUT_DRV=1, NRES_OUT_DAT=1 */ + +#if SYSCLK == 73728000 +ExtMem_Ctrl: + .word 0x000000e8 /* fuer FPGA 32 Bit konfiguriert */ +SDRAM_Ctrl: +// .word 0x28fc0037 /* default */ + .word 0xaef40027 /* p2001_bit_compact */ +WaitState_Ext: + .word 0xa0001245 /* fuer 73 MHz */ +// .word 0x0000fff3 /* rom bootloader */ +WaitState_Asic: + .word 0x00ff8a5f /* fuer 85 MHz */ +// .word 0x00000203 /* rom bootloader */ +PLL_12288_config: + .word 0x00000004 /* fuer 73 MHz */ +DIV_12288_config: + .word 0x00010601 /* fuer 73 MHz */ +PLL_12000_config: + .word 0x10004e75 /* fuer 85 MHz */ +FSC_CONFIG: + .word 0xc0000005 /* fuer 73 MHz */ +#else +#error "Please define proper timings and wait states for that sysclk." +#endif + +SP_SVC: + .word 0x40fffffc + + + +#ifndef NORELOCATE +/************************************************************************** +RELOCATE_TO - relocate etherboot to the specified address +**************************************************************************/ + .global relocate_to + +relocate_to: + ldr r1, =_start + ldr r2, =_end + + /* while (r1 < r2) { *(r0++) = *(r1++) } */ +_relocate_loop: + cmp r1, r2 + ldrcc r3, [r1], #4 + strcc r3, [r0], #4 + bcc _relocate_loop + mov pc, lr +#endif + + +.global __gccmain +__gccmain: + mov pc, lr /* return from subroutine */ diff --git a/src/arch/armnommu/drivers/net/p2001_eth.c b/src/arch/armnommu/drivers/net/p2001_eth.c new file mode 100644 index 00000000..81bc84c3 --- /dev/null +++ b/src/arch/armnommu/drivers/net/p2001_eth.c @@ -0,0 +1,525 @@ +/************************************************************************** +Etherboot - BOOTP/TFTP Bootstrap Program +P2001 NIC driver for Etherboot +***************************************************************************/ + +/* + * Copyright (C) 2004 Tobias Lorenz + * + * 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. + */ + +/* 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 ISA support functions, if this is an ISA NIC */ +#include "isa.h" + +#include "hardware.h" +#include "lxt971a.h" +#include "timer.h" + + +/* NIC specific static variables go here */ +static unsigned char MAC_HW_ADDR[6]={MAC_HW_ADDR_DRV}; + +/* DMA descriptors and buffers */ +#define NUM_RX_DESC 4 /* Number of Rx descriptor registers. */ +#define DMA_BUF_SIZE 2048 /* Buffer size */ +static DMA_DSC txd __attribute__ ((__section__(".dma.desc"))); +static DMA_DSC rxd[NUM_RX_DESC] __attribute__ ((__section__(".dma.desc"))); +static unsigned char rxb[NUM_RX_DESC * DMA_BUF_SIZE] __attribute__ ((__section__(".dma.buffer"))); +static unsigned char txb[ DMA_BUF_SIZE] __attribute__ ((__section__(".dma.buffer"))); +static unsigned int cur_rx; + +/* Device selectors */ +static unsigned int cur_channel; // DMA channel : 0..3 +static unsigned int cur_phy; // PHY Address : 0..31 +static P2001_ETH_regs_ptr EU; // Ethernet Unit : 0x0018_000 with _=0..3 +static P2001_ETH_regs_ptr MU; // Management Unit: 0x00180000 + +#define MDIO_MAXCOUNT 1000 /* mdio abort */ +static unsigned int mdio_error; /* mdio error */ + +/* Function prototypes */ +static void p2001_eth_mdio_init (); +static void p2001_eth_mdio_write(unsigned int phyadr, unsigned int regadr, unsigned int data); +static unsigned int p2001_eth_mdio_read (unsigned int phyadr, unsigned int regadr); +extern unsigned int p2001_eth_mdio_error; + +static int p2001_eth_poll (struct nic *nic, int retrieve); +static void p2001_eth_transmit (struct nic *nic, const char *d, + unsigned int t, unsigned int s, const char *p); + +static void p2001_eth_irq (struct nic *nic, irq_action_t action); + +static void p2001_eth_init (); +static void p2001_eth_disable (struct dev *dev); + +static int p2001_eth_check_link(unsigned int phy); +static int p2001_eth_probe (struct dev *dev, unsigned short *probe_addrs __unused); + + +/************************************************************************** +PHY MANAGEMENT UNIT - Read/write +***************************************************************************/ +static void p2001_eth_mdio_init() +{ + /* reset ethernet PHYs */ + printf("Resetting PHYs...\n"); + + /* GPIO24/25: TX_ER2/TX_ER0 */ + /* GPIO26/27: PHY_RESET/TX_ER1 */ + P2001_GPIO->PIN_MUX |= 0x0018; + // 31-16: 0000 1111 0000 0000 + P2001_GPIO->GPIO2_En |= 0x0400; + + P2001_GPIO->GPIO2_Out |= 0x04000000; + P2001_GPIO->GPIO2_Out &= ~0x0400; + mdelay(500); + P2001_GPIO->GPIO2_Out |= 0x0400; + + /* set management unit clock divisor */ + // max. MDIO CLK = 2.048 MHz (EU.doc) + // max. MDIO CLK = 8.000 MHz (LXT971A) + // sysclk/(2*(n+1)) = MDIO CLK <= 2.048 MHz + // n >= sysclk/4.096 MHz - 1 +#if SYSCLK == 73728000 + P2001_MU->MU_DIV = 17; // 73.728 MHZ =17=> 2.020 MHz +#else + //MU->MU_DIV = (SYSCLK/4.096)-1; +#error "Please define a proper MDIO CLK divisor for that sysclk." +#endif + asm("nop \n nop"); +} + +static void p2001_eth_mdio_write(unsigned int phyadr, unsigned int regadr, unsigned int data) +{ + static unsigned int count; + count = 0; + + /* Warten bis Hardware inaktiv (MIU = "0") */ + while ((MU->MU_CNTL & 0x8000) && (count < MDIO_MAXCOUNT)) + count++; + + /* Schreiben MU_DATA */ + MU->MU_DATA = data; + + /* Schreiben MU_CNTL */ + MU->MU_CNTL = regadr + (phyadr<<5) + (1<<10); + + /* Warten bis Hardware aktiv (MIU = "1") */ + while (((MU->MU_CNTL & 0x8000) == 0) && (count < MDIO_MAXCOUNT)) + count++; + //asm("nop \r\n nop"); + + /* Warten bis Hardware inaktiv (MIU = "0") */ + while ((MU->MU_CNTL & 0x8000) && (count < MDIO_MAXCOUNT)) + count++; + + mdio_error = (count >= MDIO_MAXCOUNT); +} + +static unsigned int p2001_eth_mdio_read(unsigned int phyadr, unsigned int regadr) +{ + static unsigned int count; + count = 0; + + do { + /* Warten bis Hardware inaktiv (MIU = "0") */ + while ((MU->MU_CNTL & 0x8000) && (count < MDIO_MAXCOUNT)) + count++; + + /* Schreiben MU_CNTL */ + MU->MU_CNTL = regadr + (phyadr<<5) + (2<<10); + + /* Warten bis Hardware aktiv (MIU = "1") */ + while (((MU->MU_CNTL & 0x8000) == 0) && (count < MDIO_MAXCOUNT)) + count++; + //asm("nop \r\n nop"); + + /* Warten bis Hardware inaktiv (MIU = "0") */ + while ((MU->MU_CNTL & 0x8000) && (count < MDIO_MAXCOUNT)) + count++; + + /* Fehler, wenn MDIO Read Error (MRE = "1") */ + } while ((MU->MU_CNTL & 0x4000) && (count < MDIO_MAXCOUNT)); + + /* Lesen MU_DATA */ + mdio_error = (count >= MDIO_MAXCOUNT); + return MU->MU_DATA; +} + + +/************************************************************************** +POLL - Wait for a frame +***************************************************************************/ +/* Function: p2001_eth_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 received. + * 0 if no pacet was received. + * + * Side effects: + * Returns (copies) the packet to the array nic->packet. + * Returns the length of the packet in nic->packetlen. + */ +static int p2001_eth_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 retstat = 0; + + if (rxd[cur_rx].stat & (1<<31)) // OWN + return retstat; + + if (!retrieve) + return 1; + + nic->packetlen = rxd[cur_rx].cntl & 0xffff; + + if (rxd[cur_rx].stat & ((1<<26)|(1<<25)|(1<<24)|(1<<23)|(1<<22))) { + /* corrupted packet received */ + printf("p2001_eth_poll: Corrupted packet received, stat = %X\n", + rxd[cur_rx].stat); + retstat = 0; + } else { + /* give packet to higher routine */ + memcpy(nic->packet, (rxb + cur_rx*DMA_BUF_SIZE), nic->packetlen); + retstat = 1; + } + +#ifdef DEBUG_NIC + printf("p2001_eth_poll: packet from %! to %! received\n", + (rxb+cur_rx*DMA_BUF_SIZE)+ETH_ALEN, + (rxb+cur_rx*DMA_BUF_SIZE)); +#endif + + /* disable receiver */ + // FIXME: is that ok? it can produce grave errors. + EU->RMAC_DMA_EN = 0; /* clear run bit */ + + /* return the descriptor and buffer to receive ring */ + rxd[cur_rx].stat = (1<<31) | (1<<30) | (1<<29); // DSC0 OWN|START|END + rxd[cur_rx].cntl = (1<<23); // DSC1 RECEIVE + rxd[cur_rx].cntl |= cur_channel << 16; // DSC1 CHANNEL + rxd[cur_rx].cntl |= DMA_BUF_SIZE; // DSC1 LEN + + if (++cur_rx == NUM_RX_DESC) + cur_rx = 0; + + /* enable receiver */ + if (!(EU->RMAC_DMA_EN & 0x01)) + EU->RMAC_DMA_EN = 0x01; /* set run bit */ + +#ifdef DEBUG_NIC + printf("RMAC_MIB0..5: %d:%d:%d:%d:%d:%d\n", + EU->RMAC_MIB0, EU->RMAC_MIB1, + EU->RMAC_MIB2, EU->RMAC_MIB3, + EU->RMAC_MIB4, EU->RMAC_MIB5); +#endif + + return retstat; /* initially as this is called to flush the input */ +} + + +/************************************************************************** +TRANSMIT - Transmit a frame +***************************************************************************/ +/* Function: p2001_eth_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 p2001_eth_transmit( + struct nic *nic __unused, + const char *d, /* Destination */ + unsigned int t, /* Type */ + unsigned int s, /* size */ + const char *p) /* Packet */ +{ + unsigned int nstype; +#ifdef DEBUG_NIC + unsigned int status; +#endif + + /* assemble packet */ + memcpy(txb, d, ETH_ALEN); // destination + memcpy(txb+ETH_ALEN, nic->node_addr, ETH_ALEN); // source + nstype = htons(t); + memcpy(txb+2*ETH_ALEN, (char*)&nstype, 2); // type + memcpy(txb+ETH_HLEN, p, s); // packet + s += ETH_HLEN; + + /* pad to minimum packet size */ +// while (sTMAC_DMA_EN = 0x01; /* set run bit */ + while(EU->TMAC_DMA_EN & 0x01) ; /* wait */ + +#ifdef DEBUG_NIC + /* check status */ + status = EU->TMAC_DMA_STAT; + if (status & ~(0x40)) + printf("p2001_eth_transmit: dma status=0x%hx\n", status); + + printf("TMAC_MIB6..7: %d:%d\n", EU->TMAC_MIB6, EU->TMAC_MIB7); +#endif +} + + +/************************************************************************** +IRQ - Enable, Disable or Force Interrupts +***************************************************************************/ +/* Function: p2001_eth_irq + * + * Description: Enable, Disable, or Force, interrupts + * + * Arguments: struct nic *nic: NIC data structure + * irq_action_t action: Requested action + * + * Returns: void. + */ + +static void +p2001_eth_irq(struct nic *nic __unused, irq_action_t action __unused) +{ + switch ( action ) { + case DISABLE : + break; + case ENABLE : + break; + case FORCE : + break; + } +} + + +/************************************************************************** +INIT - Initialize device +***************************************************************************/ +/* Function: p2001_init + * + * Description: resets the ethernet controller chip and various + * data structures required for sending and receiving packets. + * + * returns: void. + */ +static void p2001_eth_init() +{ + static int i; + + /* disable transceiver */ +// EU->TMAC_DMA_EN = 0; /* clear run bit */ +// EU->RMAC_DMA_EN = 0; /* clear run bit */ + + /* set rx filter (physical mac addresses) */ + EU->RMAC_PHYU = + (MAC_HW_ADDR[0]<< 8) + + (MAC_HW_ADDR[1]<< 0); + EU->RMAC_PHYL = + (MAC_HW_ADDR[2]<<24) + + (MAC_HW_ADDR[3]<<16) + + (MAC_HW_ADDR[4]<<8 ) + + (MAC_HW_ADDR[5]<<0 ); + + /* initialize the tx descriptor ring */ +// txd.stat = (1<<31) | (1<<30) | (1<<29); // DSC0 OWN|START|END +// txd.cntl = cur_channel << 16; // DSC1 CHANNEL +// txd.cntl |= DMA_BUF_SIZE; // DSC1 LEN + txd.buf = &txb; // DSC2 BUFFER + txd.next = &txd; // DSC3 NEXTDSC @self + EU->TMAC_DMA_DESC = &txd; + + /* initialize the rx descriptor ring */ + cur_rx = 0; + for (i = 0; i < NUM_RX_DESC; i++) { + rxd[i].stat = (1<<31) | (1<<30) | (1<<29); // DSC0 OWN|START|END + rxd[i].cntl = (1<<23); // DSC1 RECEIVE + rxd[i].cntl |= cur_channel << 16; // DSC1 CHANNEL + rxd[i].cntl |= DMA_BUF_SIZE; // DSC1 LEN + rxd[i].buf = &rxb[i*DMA_BUF_SIZE]; // DSC2 BUFFER (EU-RX data) + rxd[i].next = &rxd[i+1]; // DSC3 NEXTDSC @next + } + rxd[NUM_RX_DESC-1].next = &rxd[0]; // DSC3 NEXTDSC @first + EU->RMAC_DMA_DESC = &rxd[0]; + + /* set transmitter mode */ + EU->TMAC_CNTL = (1<<4) | /* COI: Collision ignore */ + //(1<<3) | /* CSI: Carrier Sense ignore */ + (1<<2); /* ATP: Automatic Transmit Padding */ + + /* set receive mode */ + EU->RMAC_CNTL = (1<<3) | /* BROAD: Broadcast packets */ + (1<<1); /* PHY : Packets to out MAC address */ + + /* enable receiver */ + EU->RMAC_DMA_EN = 1; /* set run bit */ +} + + +/************************************************************************** +DISABLE - Turn off ethernet interface +***************************************************************************/ +static void p2001_eth_disable(struct dev *dev __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. + */ + + /* disable transmitter */ + EU->TMAC_DMA_EN = 0; /* clear run bit */ + + /* disable receiver */ + EU->RMAC_DMA_EN = 0; /* clear run bit */ +} + + +/************************************************************************** +LINK - Check for valid link +***************************************************************************/ +static int p2001_eth_check_link(unsigned int phy) +{ + static int status; + static unsigned int count; + count = 0; + + /* Use 0x3300 for restarting NWay */ + printf("Starting auto-negotiation... "); + p2001_eth_mdio_write(phy, Adr_LXT971A_Control, 0x3300); + if (mdio_error) + goto failed; + + /* Bits 1.5 and 17.7 are set to 1 once the Auto-Negotiation process to completed. */ + do { + mdelay(500); + status = p2001_eth_mdio_read(phy, Adr_LXT971A_Status1); + if (mdio_error || (count++ > 6)) // 6*500ms = 3s timeout + goto failed; + } while (!(status & 0x20)); + + /* Bits 1.2 and 17.10 are set to 1 once the link is established. */ + if (p2001_eth_mdio_read(phy, Adr_LXT971A_Status1) & 0x04) { + /* Bits 17.14 and 17.9 can be used to determine the link operation conditions (speed and duplex). */ + printf("Valid link, operating at: %sMb-%s\n", + (p2001_eth_mdio_read(phy, Adr_LXT971A_Status2) & 0x4000) ? "100" : "10", + (p2001_eth_mdio_read(phy, Adr_LXT971A_Status2) & 0x0200) ? "FD" : "HD"); + return 1; + } + +failed: + if (mdio_error) + printf("Failed\n"); + else + printf("No valid link\n"); + return 0; +} + + +/************************************************************************** +PROBE - Look for an adapter, this routine's visible to the outside +***************************************************************************/ +static int p2001_eth_probe(struct dev *dev, unsigned short *probe_addrs __unused) +{ + struct nic *nic = (struct nic *)dev; + /* if probe_addrs is 0, then routine can use a hardwired default */ + static int board_found; + static int valid_link; + + /* reset phys and configure mdio clk */ + p2001_eth_mdio_init(); + + /* find the correct PHY/DMA/MAC combination */ + MU = P2001_MU; // MU for all PHYs is only in EU0 + printf("Searching for P2001 NICs...\n"); + for (cur_channel=0; cur_channel<4; cur_channel++) { + switch(cur_channel) { + case 0: + EU = P2001_EU0; + cur_phy = 0; + break; + case 1: + EU = P2001_EU1; + cur_phy = 1; + break; + case 2: + EU = P2001_EU2; + cur_phy = 2; + break; + case 3: + EU = P2001_EU3; + cur_phy = 3; + break; + } + + /* first a non destructive test for initial value RMAC_TLEN=1518 */ + board_found = (EU->RMAC_TLEN == 1518); + if (board_found) { + printf("Checking EU%d...\n", cur_channel); + + valid_link = p2001_eth_check_link(cur_phy); + if (valid_link) { + /* initialize device */ + p2001_eth_init(nic); + + /* set node address */ + printf("Setting MAC address to %!\n", MAC_HW_ADDR); + memcpy(nic->node_addr, MAC_HW_ADDR, 6); + + /* point to NIC specific routines */ + dev->disable = p2001_eth_disable; + nic->poll = p2001_eth_poll; + nic->transmit = p2001_eth_transmit; + nic->irq = p2001_eth_irq; + + /* Report the ISA pnp id of the board */ + dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR); + dev->devid.vendor_id = htons(0x1234); + return 1; + } + } + } + /* else */ + return 0; +} + +ISA_ROM("p2001_eth", "P2001 Ethernet Driver") +static struct isa_driver p2001_eth_driver __isa_driver = { + .type = NIC_DRIVER, + .name = "P2001 Ethernet Driver", + .probe = p2001_eth_probe, + .ioaddrs = 0, +}; diff --git a/src/arch/armnommu/include/bits/byteswap.h b/src/arch/armnommu/include/bits/byteswap.h new file mode 100644 index 00000000..2aae0800 --- /dev/null +++ b/src/arch/armnommu/include/bits/byteswap.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * 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_BITS_BYTESWAP_H +#define ETHERBOOT_BITS_BYTESWAP_H + +/* We do not have byte swap functions ... We are + * RISC processor ... + */ + +static inline unsigned short __swap16(volatile unsigned short v) +{ + return ((v << 8) | (v >> 8)); +} + +static inline unsigned int __swap32(volatile unsigned long v) +{ + return ((v << 24) | ((v & 0xff00) << 8) | ((v & 0xff0000) >> 8) | (v >> 24)); +} + +#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_16(x) \ + (__extension__ \ + ({ unsigned short int __bsx = (x); \ + ((((__bsx) >> 8) & 0xff) | (((__bsx) & 0xff) << 8)); })) + + +# define __bswap_32(x) \ + (__extension__ \ + ({ unsigned int __bsx = (x); \ + ((((__bsx) & 0xff000000) >> 24) | (((__bsx) & 0x00ff0000) >> 8) | \ + (((__bsx) & 0x0000ff00) << 8) | (((__bsx) & 0x000000ff) << 24)); })) + +#endif /* ETHERBOOT_BITS_BYTESWAP_H */ diff --git a/src/arch/armnommu/include/bits/cpu.h b/src/arch/armnommu/include/bits/cpu.h new file mode 100644 index 00000000..9516a600 --- /dev/null +++ b/src/arch/armnommu/include/bits/cpu.h @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * 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 ARM_BITS_CPU_H +#define ARM_BITS_CPU_H + +#define cpu_setup() do {} while(0) + +#endif /* ARM_BITS_CPU_H */ diff --git a/src/arch/armnommu/include/bits/elf.h b/src/arch/armnommu/include/bits/elf.h new file mode 100644 index 00000000..e0a17aa5 --- /dev/null +++ b/src/arch/armnommu/include/bits/elf.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * 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 ARM_BITS_ELF_H +#define ARM_BITS_ELF_H + +/* ELF Defines for the current architecture */ +#define EM_CURRENT EM_ARM +#define ELFDATA_CURRENT ELFDATA2LSB + +#define ELF_CHECK_ARCH(x) \ + ((x).e_machine == EM_CURRENT) + +#endif /* ARM_BITS_ELF_H */ diff --git a/src/arch/armnommu/include/bits/endian.h b/src/arch/armnommu/include/bits/endian.h new file mode 100644 index 00000000..0a18d97d --- /dev/null +++ b/src/arch/armnommu/include/bits/endian.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * 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_BITS_ENDIAN_H +#define ETHERBOOT_BITS_ENDIAN_H + +#ifdef __ARMEB__ +#define __BYTE_ORDER __BIG_ENDIAN +#else +#define __BYTE_ORDER __LITTLE_ENDIAN +#endif + +#endif /* ETHERBOOT_BITS_ENDIAN_H */ diff --git a/src/arch/armnommu/include/bits/string.h b/src/arch/armnommu/include/bits/string.h new file mode 100644 index 00000000..9cfce809 --- /dev/null +++ b/src/arch/armnommu/include/bits/string.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * 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_BITS_STRING_H +#define ETHERBOOT_BITS_STRING_H + +#endif /* ETHERBOOT_BITS_STRING_H */ diff --git a/src/arch/armnommu/include/callbacks_arch.h b/src/arch/armnommu/include/callbacks_arch.h new file mode 100644 index 00000000..fa882cb5 --- /dev/null +++ b/src/arch/armnommu/include/callbacks_arch.h @@ -0,0 +1 @@ +/* empty file */ diff --git a/src/arch/armnommu/include/hardware.h b/src/arch/armnommu/include/hardware.h new file mode 100644 index 00000000..203b78b7 --- /dev/null +++ b/src/arch/armnommu/include/hardware.h @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * 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. + */ + +/* + * Architecture: ARM9TDMI + * Processor : P2001 + */ + +#ifndef ARCH_HARDWARE_H +#define ARCH_HARDWARE_H + +#ifndef __ASSEMBLY__ + +/* DMA descriptor */ +typedef struct { + unsigned int stat; /* status: own, start, end, offset, status */ + unsigned int cntl; /* control: loop, int, type, channel, length */ + char *buf; /* buffer */ + void *next; /* nextdsc */ +} DMA_DSC; + + +/* The address definitions are from asic_bf.h */ +typedef struct { // 0x00100000U + volatile unsigned int reserved1[0x3]; + volatile unsigned int ArmDmaPri; // 0x0000000CU + volatile unsigned int SDRAM_Ctrl; // 0x00000010U + volatile unsigned int ExtMem_Ctrl; // 0x00000014U + volatile unsigned int WaitState_Ext; // 0x00000018U + volatile unsigned int WaitState_Asic; // 0x0000001CU + volatile unsigned int TOP; // 0x00000020U + volatile unsigned int reserved2[0x3]; + volatile unsigned int Adr1_EQ_30Bit; // 0x00000030U + volatile unsigned int Adr2_EQ_30Bit; // 0x00000034U + volatile unsigned int Adr3_EQ_30Bit; // 0x00000038U + volatile unsigned int Dat3_EQ_32Bit; // 0x0000003CU + volatile unsigned int Adr4_HE_20Bit; // 0x00000040U + volatile unsigned int Adr4_LT_20Bit; // 0x00000044U + volatile unsigned int Adr5_HE_20Bit; // 0x00000048U + volatile unsigned int Adr5_LT_20Bit; // 0x0000004CU + volatile unsigned int Adr_Control; // 0x00000050U + volatile unsigned int ABORT_IA_32Bit; // 0x00000054U +} *P2001_SYS_regs_ptr; +#define P2001_SYS ((volatile P2001_SYS_regs_ptr) 0x00100000) + +typedef struct { // 0x00110000U + volatile unsigned int Timer1; // 0x00000000U + volatile unsigned int Timer2; // 0x00000004U + volatile unsigned int TIMER_PRELOAD; // 0x00000008U + volatile unsigned int Timer12_PreDiv; // 0x0000000CU + volatile unsigned int TIMER_INT; // 0x00000010U + volatile unsigned int Freerun_Timer; // 0x00000014U + volatile unsigned int WatchDog_Timer; // 0x00000018U + volatile unsigned int PWM_CNT; // 0x00000020U + volatile unsigned int PWM_CNT2; // 0x00000024U + volatile unsigned int PLL_12000_config; // 0x00000030U + volatile unsigned int PLL_12288_config; // 0x00000034U + volatile unsigned int DIV_12288_config; // 0x00000038U + volatile unsigned int MOD_CNT_768; // 0x0000003CU + volatile unsigned int FSC_IRQ_STATUS; // 0x00000040U + volatile unsigned int FSC_CONFIG; // 0x00000044U + volatile unsigned int FSC_CONSTRUCT; // 0x00000048U + volatile unsigned int FSC_base_clk_reg; // 0x0000004CU + volatile unsigned int SYSCLK_SHAPE; // 0x00000050U + volatile unsigned int SDRAMCLK_SHAPE; // 0x00000054U + volatile unsigned int RING_OSZI; // 0x00000058U +} *P2001_TIMER_regs_ptr; +#define P2001_TIMER ((volatile P2001_TIMER_regs_ptr) 0x00110000) + +typedef struct { // 0x00120000U + volatile unsigned int reserved1[0x5]; + volatile unsigned int GPIO_Config; // 0x00000014U + volatile unsigned int GPIO_INT; // 0x00000018U + volatile unsigned int GPIO_Out; // 0x0000001CU + volatile unsigned int GPIO_IN; // 0x00000020U + volatile unsigned int GPIO_En; // 0x00000024U + volatile unsigned int PIN_MUX; // 0x00000028U + volatile unsigned int NRES_OUT; // 0x0000002CU + volatile unsigned int GPIO2_Out; // 0x00000030U + volatile unsigned int GPIO2_IN; // 0x00000034U + volatile unsigned int GPIO2_En; // 0x00000038U + volatile unsigned int GPIO_INT_SEL; // 0x0000003CU + volatile unsigned int GPI3_IN; // 0x00000040U + volatile unsigned int GPO4_OUT; // 0x00000044U +} *P2001_GPIO_regs_ptr; +#define P2001_GPIO ((volatile P2001_GPIO_regs_ptr) 0x00120000) + +typedef struct { // 0x00130000U + volatile unsigned int Main_NFIQ_Int_Ctrl; // 0x00000000U + volatile unsigned int Main_NIRQ_Int_Ctrl; // 0x00000004U + volatile unsigned int Status_NFIQ; // 0x00000008U + volatile unsigned int Status_NIRQ; // 0x0000000CU +} *P2001_INT_CTRL_regs_ptr; +#define P2001_INT_CTRL ((volatile P2001_INT_CTRL_regs_ptr) 0x00130000) + +typedef union { // 0x00140000U + struct { // write + volatile unsigned int TX1; // 0x00000000U + volatile unsigned int TX2; // 0x00000004U + volatile unsigned int TX3; // 0x00000008U + volatile unsigned int TX4; // 0x0000000CU + volatile unsigned int Baudrate; // 0x00000010U + volatile unsigned int reserved1[0x3]; + volatile unsigned int Config; // 0x00000020U + volatile unsigned int Clear; // 0x00000024U + volatile unsigned int Echo_EN; // 0x00000028U + volatile unsigned int IRQ_Status; // 0x0000002CU + } w; // write + + struct { // read + volatile unsigned int RX1; // 0x00000000U + volatile unsigned int RX2; // 0x00000004U + volatile unsigned int RX3; // 0x00000008U + volatile unsigned int RX4; // 0x0000000CU + volatile unsigned int reserved1[0x4]; + volatile unsigned int PRE_STATUS; // 0x00000020U + volatile unsigned int STATUS; // 0x00000024U + volatile unsigned int reserved2[0x1]; + volatile unsigned int IRQ_Status; // 0x0000002CU + } r; // read +} *P2001_UART_regs_ptr; +#define P2001_UART ((volatile P2001_UART_regs_ptr) 0x00140000) + +typedef struct { // 0x0018_000U _=0,1,2,3 + volatile DMA_DSC * RMAC_DMA_DESC; // 0x00000000U + volatile unsigned int RMAC_DMA_CNTL; // 0x00000004U + volatile unsigned int RMAC_DMA_STAT; // 0x00000008U + volatile unsigned int RMAC_DMA_EN; // 0x0000000CU + volatile unsigned int RMAC_CNTL; // 0x00000010U + volatile unsigned int RMAC_TLEN; // 0x00000014U + volatile unsigned int RMAC_PHYU; // 0x00000018U + volatile unsigned int RMAC_PHYL; // 0x0000001CU + volatile unsigned int RMAC_PFM0; // 0x00000020U + volatile unsigned int RMAC_PFM1; // 0x00000024U + volatile unsigned int RMAC_PFM2; // 0x00000028U + volatile unsigned int RMAC_PFM3; // 0x0000002CU + volatile unsigned int RMAC_PFM4; // 0x00000030U + volatile unsigned int RMAC_PFM5; // 0x00000034U + volatile unsigned int RMAC_PFM6; // 0x00000038U + volatile unsigned int RMAC_PFM7; // 0x0000003CU + volatile unsigned int RMAC_MIB0; // 0x00000040U + volatile unsigned int RMAC_MIB1; // 0x00000044U + volatile unsigned int RMAC_MIB2; // 0x00000048U + volatile unsigned int RMAC_MIB3; // 0x0000004CU + volatile unsigned int RMAC_MIB4; // 0x00000050U + volatile unsigned int RMAC_MIB5; // 0x00000054U + volatile unsigned int reserved1[0x1e8]; + volatile unsigned int RMAC_DMA_DATA; // 0x000007F8U + volatile unsigned int RMAC_DMA_ADR; // 0x000007FCU + volatile DMA_DSC * TMAC_DMA_DESC; // 0x00000800U + volatile unsigned int TMAC_DMA_CNTL; // 0x00000804U + volatile unsigned int TMAC_DMA_STAT; // 0x00000808U + volatile unsigned int TMAC_DMA_EN; // 0x0000080CU + volatile unsigned int TMAC_CNTL; // 0x00000810U + volatile unsigned int TMAC_MIB6; // 0x00000814U + volatile unsigned int TMAC_MIB7; // 0x00000818U + volatile unsigned int reserved2[0x1]; + volatile unsigned int MU_CNTL; // 0x00000820U + volatile unsigned int MU_DATA; // 0x00000824U + volatile unsigned int MU_DIV; // 0x00000828U + volatile unsigned int CONF_RMII; // 0x0000082CU + volatile unsigned int reserved3[0x1f2]; + volatile unsigned int TMAC_DMA_DATA; // 0x00000FF8U + volatile unsigned int TMAC_DMA_ADR; // 0x00000FFCU +} *P2001_ETH_regs_ptr; +#define P2001_EU0 ((volatile P2001_ETH_regs_ptr) 0x00180000) +#define P2001_EU1 ((volatile P2001_ETH_regs_ptr) 0x00181000) +#define P2001_EU2 ((volatile P2001_ETH_regs_ptr) 0x00182000) +#define P2001_EU3 ((volatile P2001_ETH_regs_ptr) 0x00183000) +#define P2001_MU P2001_EU0 + +#endif + +#endif /* ARCH_HARDWARE_H */ diff --git a/src/arch/armnommu/include/hooks.h b/src/arch/armnommu/include/hooks.h new file mode 100644 index 00000000..cdcf023b --- /dev/null +++ b/src/arch/armnommu/include/hooks.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * 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_ARM_HOOKS_H +#define ETHERBOOT_ARM_HOOKS_H + +struct Elf_Bhdr; + +#define arch_main(data, params) do {} while(0) +//void arch_main(in_call_data_t *data, va_list params); + +#define arch_on_exit(status) do {} while(0) +//void arch_on_exit(int status); + +#define arch_relocate_to(addr) do {} while(0) +//void arch_relocate_to(unsigned long addr); + +#define arch_relocated_from(old_addr) do {} while(0) +//void arch_relocate_from(unsigned long old_addr); + +#endif /* ETHERBOOT_ARM_HOOKS_H */ diff --git a/src/arch/armnommu/include/io.h b/src/arch/armnommu/include/io.h new file mode 100644 index 00000000..478e443d --- /dev/null +++ b/src/arch/armnommu/include/io.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * 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_IO_H +#define ETHERBOOT_IO_H + +#define virt_to_phys(vaddr) ((unsigned long) (vaddr)) +#define phys_to_virt(vaddr) ((void *) (vaddr)) + +#define virt_to_bus virt_to_phys +#define bus_to_virt phys_to_virt + +#define iounmap(addr) ((void)0) +#define ioremap(physaddr, size) (physaddr) + +extern unsigned char inb (unsigned long int port); +extern unsigned short int inw (unsigned long int port); +extern unsigned long int inl (unsigned long int port); +extern void outb (unsigned char value, unsigned long int port); +extern void outw (unsigned short value, unsigned long int port); +extern void outl (unsigned long value, unsigned long int port); + +#endif /* ETHERBOOT_IO_H */ diff --git a/src/arch/armnommu/include/latch.h b/src/arch/armnommu/include/latch.h new file mode 100644 index 00000000..23ceaa30 --- /dev/null +++ b/src/arch/armnommu/include/latch.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * 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 LATCH_H +#define LATCH_H + +// Freerun_Timer is always at 12.288 MHZ +#define TICKS_PER_SEC (12288000UL) +//#define TICKS_PER_SEC (73728000UL) + +#endif /* LATCH_H */ diff --git a/src/arch/armnommu/include/limits.h b/src/arch/armnommu/include/limits.h new file mode 100644 index 00000000..8df03ef2 --- /dev/null +++ b/src/arch/armnommu/include/limits.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * 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 __LIMITS_H +#define __LIMITS_H 1 + +#define MB_LEN_MAX 16 + +#define CHAR_BIT 8 + +#define SCHAR_MIN (-128) +#define SCHAR_MAX 127 + +#define UCHAR_MAX 255 + +#define CHAR_MIN SCHAR_MIN +#define CHAR_MAX SCHAR_MAX + +#define SHRT_MIN (-32768) +#define SHRT_MAX 32767 + +#define USHRT_MAX 65535 + +#define INT_MIN (-INT_MAX - 1) +#define INT_MAX 2147483647 + +#define UINT_MAX 4294967295U + +#define LONG_MAX 2147483647L +#define LONG_MIN (-LONG_MAX - 1L) + +#define ULONG_MAX 4294967295UL + +#define LLONG_MAX 9223372036854775807LL +#define LLONG_MIN (-LLONG_MAX - 1LL) + +#define ULLONG_MAX 18446744073709551615ULL + +#endif diff --git a/src/arch/armnommu/include/lxt971a.h b/src/arch/armnommu/include/lxt971a.h new file mode 100644 index 00000000..16314ec2 --- /dev/null +++ b/src/arch/armnommu/include/lxt971a.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * 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. + */ + +/* + * Intel LXT971ALE (MII-compatible PHY) + */ + +#define Adr_LXT971A_Control 0 /* Control Register */ +#define Adr_LXT971A_Status1 1 /* MII Status Register #1 */ +#define Adr_LXT971A_PHY_ID1 2 /* PHY Identification Register 1 */ +#define Adr_LXT971A_PHY_ID2 3 /* PHY Identification Register 2 */ +#define Adr_LXT971A_AN_Advertise 4 /* Auto Negotiation Advertisement Register */ +#define Adr_LXT971A_AN_Link_Ability 5 /* Auto Negotiation Link Partner Base Page Ability Register */ +#define Adr_LXT971A_AN_Expansion 6 /* Auto Negotiation Expansion */ +#define Adr_LXT971A_AN_Next_Page_Txmit 7 /* Auto Negotiation Next Page Transmit Register */ +#define Adr_LXT971A_AN_Link_Next_Page 8 /* Auto Negotiation Link Partner Next Page Receive Register */ +#define Adr_LXT971A_Fast_Control 9 /* Not Implemented */ +#define Adr_LXT971A_Fast_Status 10 /* Not Implemented */ +#define Adr_LXT971A_Extended_Status 15 /* Not Implemented */ +#define Adr_LXT971A_Port_Config 16 /* Configuration Register */ +#define Adr_LXT971A_Status2 17 /* Status Register #2 */ +#define Adr_LXT971A_Interrupt_Enable 18 /* Interrupt Enable Register */ +#define Adr_LXT971A_Interrupt_Status 19 /* Interrupt Status Register */ +#define Adr_LXT971A_LED_Config 20 /* LED Configuration Register */ +#define Adr_LXT971A_Transmit_Control 30 /* Transmit Control Register */ diff --git a/src/arch/armnommu/include/setjmp.h b/src/arch/armnommu/include/setjmp.h new file mode 100644 index 00000000..6499c488 --- /dev/null +++ b/src/arch/armnommu/include/setjmp.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * 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_SETJMP_H +#define ETHERBOOT_SETJMP_H + +#ifndef __ASSEMBLER__ +/* Jump buffer contains v1-v6, sl, fp, sp and pc. Other registers are not + saved. */ +//typedef int jmp_buf[22]; +typedef int jmp_buf[10]; +#endif + +extern int sigsetjmp(jmp_buf __env, int __savemask); +extern void longjmp(jmp_buf __env, int __val) __attribute__((__noreturn__)); + +#define setjmp(env) sigsetjmp(env,0) + +#endif /* ETHERBOOT_SETJMP_H */ diff --git a/src/arch/armnommu/include/stdint.h b/src/arch/armnommu/include/stdint.h new file mode 100644 index 00000000..3f5dc3f9 --- /dev/null +++ b/src/arch/armnommu/include/stdint.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * 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 STDINT_H +#define STDINT_H + +typedef unsigned long size_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 int int32_t; +typedef signed long long int64_t; + +#endif /* STDINT_H */ diff --git a/src/arch/e1/Config b/src/arch/e1/Config new file mode 100644 index 00000000..69a7a978 --- /dev/null +++ b/src/arch/e1/Config @@ -0,0 +1,7 @@ +# Config for e1 Etherboot +# + +CFLAGS+= -mgnu-param -DCONSOLE_SERIAL -DCOMCONSOLE=0x01C00000 -DCONSPEED=28800 +CFLAGS+= -DSIZEINDICATOR -DNO_DHCP_SUPPORT -DBAR_PROGRESS +#CFLAGS+= -DEMBEDDED -DMAC_HW_ADDR_DRV="'H','Y','L','N','X','1'" + diff --git a/src/arch/e1/Makefile b/src/arch/e1/Makefile new file mode 100644 index 00000000..a7c6f3b8 --- /dev/null +++ b/src/arch/e1/Makefile @@ -0,0 +1,70 @@ +ARCH_FORMAT= coff-e1 + +BUILD_ROMS= $(ROMS) +BUILD_COFFS= $(patsubst %img, %coff, $(IMGS)) +SUFFIXES+= rom zrom coff + +CC= e1-coff-gcc +AS= e1-coff-as +LD= e1-coff-ld +SIZE= e1-coff-size +AR= e1-coff-ar +RANLIB= e1-coff-ranlib +OBJCOPY=e1-coff-objcopy + +# DMAC_HW_ADDR_DRV holds the ethernet's MAC address. It is passed as +# flag to the low level driver instead of reading it from an +# external EEPROM, which we do not have! +EXTRA_CFLAGS = -DEMBEDDED -DMAC_HW_ADDR_DRV="'H','Y','L','N','X','1'" + +START= $(BIN)/start.o +START16= $(BIN)/start.o + +SRCS+= arch/e1/core/e132_xs.c +SRCS+= arch/e1/core/e1_timer.c +SRCS+= arch/e1/core/longjmp.c +SRCS+= arch/e1/core/memcmp.S +SRCS+= arch/e1/core/memcpy.S +SRCS+= arch/e1/core/memset.S +SRCS+= arch/e1/core/setjmp.c +SRCS+= arch/e1/core/strcmp.S +SRCS+= arch/e1/core/start.S + +ROMLIMIT:=3276800 + +include $(BIN)/Roms + +ROMS= $(BIN)/cs89x0.rom +IMGS= $(BIN)/cs89x0.img + +#allfiles: $(BUILD_ROMS) +all: $(BUILD_COFFS) + +BOBJS+= $(BIN)/e1_timer.o +BOBJS+= $(BIN)/memcmp.o $(BIN)/memcpy.o $(BIN)/memset.o +BOBJS+= $(BIN)/setjmp.o $(BIN)/longjmp.o +BOBJS+= $(BIN)/e132_xs.o + +# Utilities + +$(BIN)/nrv2b: util/nrv2b.c + $(HOST_CC) -O2 -DENCODE -DDECODE -DMAIN -DVERBOSE -DNDEBUG -DBITSIZE=32 -DENDIAN=0 -o $@ $< + +# Pattern Rules +# General for compiling/assembly source files +$(BIN)/cs89x0.o: drivers/net/cs89x0.c $(MAKEDEPS) + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -o $@ -c $< +# With the current tools we have problem with the compilation +# of the vsprintf file when the -O2 is selected. So we compile +# the aforemntioned file with -O1 !!! +$(BIN)/vsprintf.o: core/vsprintf.c $(MAKEDEPS) + $(CC) $(CFLAGS) -O1 -o $@ -c $< + +$(BIN)/%.o: arch/e1/core/%.c $(MAKEDEPS) + $(CC) $(CFLAGS) -o $@ -c $< + +$(BIN)/%.o: arch/e1/core/%.S $(MAKEDEPS) + $(CPP) $(CFLAGS) -D ASSEMBLY $< | $(AS) $(ASFLAGS) -o $@ + +$(BIN)/%.coff: $(BIN)/%.tmp $(MAKEDEPS) + mv $< $(BIN)/etherboot.coff diff --git a/src/arch/e1/Makefile.working b/src/arch/e1/Makefile.working new file mode 100644 index 00000000..a0ebefe5 --- /dev/null +++ b/src/arch/e1/Makefile.working @@ -0,0 +1,67 @@ +ARCH_FORMAT= coff-e1 + +CC= e1-coff-gcc +AS= e1-coff-as +LD= e1-coff-ld +SIZE= e1-coff-size +AR= e1-coff-ar +RANLIB= e1-coff-ranlib +OBJCOPY=e1-coff-objcopy + +EXTRA_CFLAGS = -DEMBEDDED -DMAC_HW_ADDR_DRV="'H','Y','L','N','X','1'" + +BUILD_ROMS= $(ROMS) +BUILD_COFFS= $(BIN)/cs89x0.coff +#BUILD_COFFS= $(patsubst %img, %coff, $(IMGS)) + +START= $(BIN)/start.o +START16= $(BIN)/start.o + +#SRCS+= arch/e1/core/coff_loader.c +SRCS+= arch/e1/core/e132_xs.c +SRCS+= arch/e1/core/e1_timer.c +SRCS+= arch/e1/core/longjmp.c +SRCS+= arch/e1/core/memcmp.S +SRCS+= arch/e1/core/memcpy.S +SRCS+= arch/e1/core/memset.S +SRCS+= arch/e1/core/setjmp.c +SRCS+= arch/e1/core/strcmp.S +SRCS+= arch/e1/core/start.S + +ROMLIMIT:=3276800 + +include $(BIN)/Roms + +hyperstone: $(BUILD_COFFS) +coff: $(BUILD_COFFS) + +BOBJS+= $(BIN)/e1_timer.o +BOBJS+= $(BIN)/memcmp.o $(BIN)/memcpy.o $(BIN)/memset.o +BOBJS+= $(BIN)/setjmp.o $(BIN)/longjmp.o +BOBJS+= $(BIN)/e132_xs.o + +# Utilities + +$(BIN)/nrv2b: util/nrv2b.c + $(HOST_CC) -O2 -DENCODE -DDECODE -DMAIN -DVERBOSE -DNDEBUG -DBITSIZE=32 -DENDIAN=0 -o $@ $< + +# Pattern Rules +# General for compiling/assembly source files + +$(BIN)/cs89x0.o: drivers/net/cs89x0.c $(MAKEDEPS) + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -o $@ -c $< +# With the current tools we have problem with the compilation +# of the vsprintf file when the -O2 is selected. So we compile +# the aforemntioned file with -O1 !!! +$(BIN)/vsprintf.o: core/vsprintf.c $(MAKEDEPS) + $(CC) $(CFLAGS) -O1 -o $@ -c $< + +$(BIN)/%.o: arch/e1/core/%.c $(MAKEDEPS) + $(CC) $(CFLAGS) -o $@ -c $< + +$(BIN)/%.o: arch/e1/core/%.S $(MAKEDEPS) + $(CPP) $(CFLAGS) -D ASSEMBLY $< | $(AS) $(ASFLAGS) -o $@ + +$(BIN)/%.coff: $(BIN)/%.tmp $(MAKEDEPS) + mv $< $(BIN)/etherboot.coff + diff --git a/src/arch/e1/README b/src/arch/e1/README new file mode 100644 index 00000000..cdf676a8 --- /dev/null +++ b/src/arch/e1/README @@ -0,0 +1,80 @@ +Introduction +--------------------- +This README file provides guideliness to compile successfully the +Etherboot for Hyperstone. +This directory (src/arch/e1) contains the files that depend on +Hyperstone's architecture. The header files that include the +configuration of the system are based on Hyperstone's E132-XS +development board. The can be very easily modified to support +anyother configuration environment. + +Software Perquisites: +--------------------- +The build environment requires a C compiler for the E1-32XS +processor. Normally you can simply install a cross-compiling tool +chain based on the GNU tools (that is binutils and gcc). If you +are running a Linux system on a x86 CPU then you can just download +the toolchain as a binary from Hyperstone's official web-site. The +binary distribution will untar the tools in the /usr/local/e1-coff +directory. On any other system you will need to build the tool +chain from the sources. + +To compile successfully the following tools should be available: + - GNU toolchain: + - GCC ver 2.95.2 20030110 (release) e1-coff-gcc -v + - LD ver 2.12.90 20020726 e1-coff-ld -V + +Hardware Perquisites: +--------------------- +The etherboot has been successfully tested in the E1-32XS +development board. A second serial device is initialized +to act as a console. The standard messages +are redirected to the console. Nevertheless, if one wants not +to use the serial console he may omit the corresponding switches +from the Config file located under "src/arch/e1/" directory. + +On the E1-32XS board that was used, a daughter-board was employed +to connect a second HyIce to the development board. Since the HyIce +embeds a standard 16550 device, the Etherboot's standard device +driver is used. + +The position of the jumpers of the development board in order to +initialize both the second HyIce and the Ethernet device is +depicted in the following table: + +Jumper: Position +------:-------------- +J3 1-2 (default) +J4 1-2 (default) +J13 5-6 +J5 1-2 (default) +J6 1-2 & 3-4 +J7 3-4 +J9 1-2 (default) +J10 1-2 +J11 3-4 + +Compilation +--------------------- +In order to compile Etherboot for Hyperstone, the following steps should be followed: +1) Edit the main Makefile (located under "src" directory") and comment-out +the ARCH variable (by putting a "#" in front of it). Append the following line: +ARCH:=e1 +2) Edit the Config file (the one located under "src" directory) and make sure that +the CFLAGS variable will contain *only* the following swithces: +CFLAGS+= -DCONFIG_ISA +CFLAGS+= -DBOOT_FIRST=BOOT_NIC +CFLAGS+= -DALLOW_ONLY_ENCAPSULATED +CFLAGS+= -DBACKOFF_LIMIT=7 -DCONGESTED +CFLAGS+= -DCOFF_IMAGE +CFLAGS+= -DDOWNLOAD_PROTO_TFTP +Please note that extra or any other switches may cause failure of compilation! +3) type "make hyperstone" or "make coff" +4) the generated file will be located under the "src/bin" directory and will be called : + "etherboot.coff". Now you may download it with usual way using e1-coff-gdb .. + +Have Fun + +Yannis Mitsos, George Thanos +{gmitsos,gthanos}@telecom.ntua.gr + diff --git a/src/arch/e1/core/coff_loader.c b/src/arch/e1/core/coff_loader.c new file mode 100644 index 00000000..d628e5ed --- /dev/null +++ b/src/arch/e1/core/coff_loader.c @@ -0,0 +1,176 @@ +/* + * Copyright 2003 Yannis Mitsos and George Thanos + * {gmitsos@gthanos}@telecom.ntua.gr + * Released under GPL2, see the file COPYING in the top directory + * COFF loader is based on the source code of the ELF loader. + * + */ +#include "coff.h" + +#define COFF_DEBUG 0 + +typedef struct { + COFF_filehdr coff32; + COFF_opthdr opthdr32; + union { + COFF_scnhdr scnhdr32[1]; + unsigned char dummy[1024]; + } p; + unsigned long curaddr; + signed int segment; /* current segment number, -1 for none */ + unsigned int loc; /* start offset of current block */ + unsigned int skip; /* padding to be skipped to current segment */ + unsigned long toread; /* remaining data to be read in the segment */ +}coff_state; + +coff_state cstate; + +static sector_t coff32_download(unsigned char *data, unsigned int len, int eof); +static inline os_download_t coff_probe(unsigned char *data, unsigned int len) +{ + unsigned long phdr_size; + + if (len < (sizeof(cstate.coff32)+ sizeof(cstate.opthdr32))) { + return 0; + } + memcpy(&cstate.coff32, data, (sizeof(cstate.coff32)+sizeof(cstate.opthdr32))); + + if ((cstate.coff32.f_magic != EM_E1) || + (cstate.opthdr32.magic != O_MAGIC)){ + return 0; + } + printf("(COFF"); + printf(")... \n"); + + if (cstate.coff32.f_opthdr == 0){ + printf("No optional header in COFF file, cannot find the entry point\n"); + return dead_download; + } + + phdr_size = cstate.coff32.f_nscns * sizeof(cstate.p.scnhdr32); + if (sizeof(cstate.coff32) + cstate.coff32.f_opthdr + phdr_size > len) { + printf("COFF header outside first block\n"); + return dead_download; + } + + memcpy(&cstate.p.scnhdr32, data + (sizeof(cstate.coff32) + cstate.coff32.f_opthdr), phdr_size); + + /* Check for Etherboot related limitations. Memory + * between _text and _end is not allowed. + * Reasons: the Etherboot code/data area. + */ + for (cstate.segment = 0; cstate.segment < cstate.coff32.f_nscns; cstate.segment++) { + unsigned long start, mid, end, istart, iend; + + if ((cstate.p.scnhdr32[cstate.segment].s_flags != S_TYPE_TEXT) && + (cstate.p.scnhdr32[cstate.segment].s_flags != S_TYPE_DATA) && + (cstate.p.scnhdr32[cstate.segment].s_flags != S_TYPE_BSS)){ /* Do we realy need to check the BSS section ? */ +#ifdef COFF_DEBUG + printf("Section <%s> in not a loadable section \n",cstate.p.scnhdr32[cstate.segment].s_name); +#endif + continue; + } + + start = cstate.p.scnhdr32[cstate.segment].s_paddr; + mid = start + cstate.p.scnhdr32[cstate.segment].s_size; + end = start + cstate.p.scnhdr32[cstate.segment].s_size; + + /* Do we need the following variables ? */ + istart = 0x8000; + iend = 0x8000; + + if (!prep_segment(start, mid, end, istart, iend)) { + return dead_download; + } +} + cstate.segment = -1; + cstate.loc = 0; + cstate.skip = 0; + cstate.toread = 0; + return coff32_download; +} + +extern int mach_boot(unsigned long entry_point); +static sector_t coff32_download(unsigned char *data, unsigned int len, int eof) +{ + unsigned long skip_sectors = 0; + unsigned int offset; /* working offset in the current data block */ + int i; + + offset = 0; + do { + if (cstate.segment != -1) { + if (cstate.skip) { + if (cstate.skip >= len - offset) { + cstate.skip -= len - offset; + break; + } + offset += cstate.skip; + cstate.skip = 0; + } + + if (cstate.toread) { + unsigned int cplen; + cplen = len - offset; + if (cplen >= cstate.toread) { + cplen = cstate.toread; + } + memcpy(phys_to_virt(cstate.curaddr), data+offset, cplen); + cstate.curaddr += cplen; + cstate.toread -= cplen; + offset += cplen; + if (cstate.toread) + break; + } + } + + /* Data left, but current segment finished - look for the next + * segment (in file offset order) that needs to be loaded. + * We can only seek forward, so select the program headers, + * in the correct order. + */ + cstate.segment = -1; + for (i = 0; i < cstate.coff32.f_nscns; i++) { + + if ((cstate.p.scnhdr32[i].s_flags != S_TYPE_TEXT) && + (cstate.p.scnhdr32[i].s_flags != S_TYPE_DATA)) + continue; + if (cstate.p.scnhdr32[i].s_size == 0) + continue; + if (cstate.p.scnhdr32[i].s_scnptr < cstate.loc + offset) + continue; /* can't go backwards */ + if ((cstate.segment != -1) && + (cstate.p.scnhdr32[i].s_scnptr >= cstate.p.scnhdr32[cstate.segment].s_scnptr)) + continue; /* search minimum file offset */ + cstate.segment = i; + } + + if (cstate.segment == -1) { + /* No more segments to be loaded, so just start the + * kernel. This saves a lot of network bandwidth if + * debug info is in the kernel but not loaded. */ + goto coff_startkernel; + break; + } + cstate.curaddr = cstate.p.scnhdr32[cstate.segment].s_paddr; + cstate.skip = cstate.p.scnhdr32[cstate.segment].s_scnptr - (cstate.loc + offset); + cstate.toread = cstate.p.scnhdr32[cstate.segment].s_size; +#if COFF_DEBUG + printf("PHDR %d, size %#lX, curaddr %#lX\n", + cstate.segment, cstate.toread, cstate.curaddr); +#endif + } while (offset < len); + + cstate.loc += len + (cstate.skip & ~0x1ff); + skip_sectors = cstate.skip >> 9; + cstate.skip &= 0x1ff; + + if (eof) { + unsigned long entry; +coff_startkernel: + entry = cstate.opthdr32.entry; + done(); + mach_boot(entry); + } + return skip_sectors; +} diff --git a/src/arch/e1/core/e132_xs.c b/src/arch/e1/core/e132_xs.c new file mode 100644 index 00000000..cc4eaae2 --- /dev/null +++ b/src/arch/e1/core/e132_xs.c @@ -0,0 +1,70 @@ +/* + * Copyright 2003 Yannis Mitsos and George Thanos + * {gmitsos@gthanos}@telecom.ntua.gr + * Released under GPL2, see the file COPYING in the top directory + * + */ +#include "hooks.h" +#include "io.h" +#include "etherboot.h" +#include "e132_xs_board.h" + +unsigned int io_periph[NR_CS] = {[0 ... NR_CS-1] = 0 }; + +/* +void arch_main(struct Elf_Bhdr *ptr __unused) +{ + +} +*/ + +void init_peripherals(void) +{ + int i; + + for(i=0; i< NR_CS; i++){ + io_periph[i]= (SLOW_IO_ACCESS | i << 22); + } + + io_periph[ETHERNET_CS] = (io_periph[ETHERNET_CS] | 1 << IOWait); + + asm volatile(" + ori SR, 0x20 + movi FCR, 0x66ffFFFF" + : + :); +} + +struct meminfo meminfo; +void get_memsizes(void) +{ +/* We initialize the meminfo structure + * according to our development board's specs + * We do not have a way to automatically probe the + * memspace instead we initialize it manually + */ + meminfo.basememsize = BASEMEM; + meminfo.memsize = SDRAM_SIZE; + meminfo.map_count = NR_MEMORY_REGNS; + + meminfo.map[0].addr = SDRAM_BASEMEM; + meminfo.map[0].size = SDRAM_SIZE; + meminfo.map[0].type = E820_RAM; + meminfo.map[1].addr = SRAM_BASEMEM; + meminfo.map[1].size = SRAM_SIZE; + meminfo.map[1].type = E820_RAM; + meminfo.map[2].addr = IRAM_BASEMEM; + meminfo.map[2].size = IRAM_SIZE; + meminfo.map[2].type = E820_RAM; +} + +int mach_boot(register unsigned long entry_point) +{ + asm volatile( + "mov PC, %0" + : /* no outputs */ + : "l" (entry_point) ); + return 0; /* We should never reach this point ! */ + +} + diff --git a/src/arch/e1/core/e1_timer.c b/src/arch/e1/core/e1_timer.c new file mode 100644 index 00000000..d3ddb644 --- /dev/null +++ b/src/arch/e1/core/e1_timer.c @@ -0,0 +1,94 @@ +/* + * Copyright 2003 Yannis Mitsos and George Thanos + * {gmitsos@gthanos}@telecom.ntua.gr + * Released under GPL2, see the file COPYING in the top directory + * + */ +#include "etherboot.h" +#include "timer.h" +#include "e132_xs_board.h" + +/* get timer returns the contents of the timer */ +static inline unsigned long get_timer(void) +{ + unsigned long result; + __asm__ __volatile__(" + ORI SR, 0x20 + mov %0, TR" + : "=l"(result)); + return result; +} + +/* ------ Calibrate the TSC ------- + * Time how long it takes to excute a loop that runs in known time. + * And find the convertion needed to get to CLOCK_TICK_RATE + */ + +static unsigned long configure_timer(void) +{ + unsigned long TPR_value; /* Timer Prescalar Value */ + + TPR_value = 0x000C00000; + + asm volatile (" + FETCH 4 + ORI SR, 0x20 + MOV TPR, %0 + ORI SR, 0x20 + MOVI TR, 0x0" + : /* no outputs */ + : "l" (TPR_value) + ); + + printf("The time prescaler register is set to: <%#x>\n",TPR_value); + return (1); +} + +static unsigned long clocks_per_tick; + +void setup_timers(void) +{ + if (!clocks_per_tick) { + clocks_per_tick = configure_timer(); + } +} + +unsigned long currticks(void) +{ + return get_timer()/clocks_per_tick; +} + +static unsigned long timer_timeout; +static int __timer_running(void) +{ + return get_timer() < timer_timeout; +} + +void udelay(unsigned int usecs) +{ + unsigned long now; + now = get_timer(); + timer_timeout = now + usecs * ((clocks_per_tick * TICKS_PER_SEC)/(1000*1000)); + while(__timer_running()); +} +void ndelay(unsigned int nsecs) +{ + unsigned long now; + now = get_timer(); + timer_timeout = now + nsecs * ((clocks_per_tick * TICKS_PER_SEC)/(1000*1000*1000)); + while(__timer_running()); +} + +void load_timer2(unsigned int timer2_ticks) +{ + unsigned long now; + unsigned long clocks; + now = get_timer(); + clocks = timer2_ticks * ((clocks_per_tick * TICKS_PER_SEC)/CLOCK_TICK_RATE); + timer_timeout = now + clocks; +} + +int timer2_running(void) +{ + return __timer_running(); +} diff --git a/src/arch/e1/core/etherboot.lds b/src/arch/e1/core/etherboot.lds new file mode 100644 index 00000000..75d2dba1 --- /dev/null +++ b/src/arch/e1/core/etherboot.lds @@ -0,0 +1,126 @@ +/* Default linker script, for normal executables */ +OUTPUT_FORMAT("coff-e1-big") +MEMORY + { + romvec : ORIGIN = 0x2000000, LENGTH = 0x0000400 + flash : ORIGIN = 0xE0000000, LENGTH = 0x20000 + eflash : ORIGIN = 0x2200000, LENGTH = 0x20000 + ram : ORIGIN = 0x00000000, LENGTH = 0x1000000 + eram16MB : ORIGIN = 0x01000000, LENGTH = 0 + sram : ORIGIN = 0x40000000, LENGTH = 0x40000 + iram : ORIGIN = 0xC0000000, LENGTH = 0x4000 + } + +SEARCH_DIR("/usr/local/e1-coff/lib"); +ENTRY(Main) +MEM0start = 0x00000000; +MEM0size = 0x40000000; +MEM1start = 0x40000000; +MEM1size = 0x40000000; +MEM2start = 0x80000000; +MEM2size = 0x40000000; +IRAMstart = 0xC0000000; +IRAMsize = 0x20000000; +MEM3start = 0xE0000000; +MEM3size = 0x20000000; +Stack1Reserve = 560; +_Stack1Size = DEFINED(_Stack1Size)? _Stack1Size : 1*1024; +_Stack2Size = DEFINED(_Stack2Size)? _Stack2Size : 16*1024; +_Stack1Base = DEFINED(_Stack1Base)? _Stack1Base : __bss_end__; +_Stack2Base = DEFINED(_Stack2Base)? _Stack2Base : __bss_end__ + _Stack1Size + Stack1Reserve; +_Mem0HeapBase = DEFINED(_Mem0HeapBase)? _Mem0HeapBase : _Stack2Base + _Stack2Size; +_Mem1HeapBase = DEFINED(_Mem1HeapBase)? _Mem1HeapBase : 0; +Priority = DEFINED(Priority) ? Priority : 31; +TextBase = DEFINED(TextBase) ? TextBase : 0xa00000; +SECTIONS +{ + .G6 (DEFINED(G6Base) ? G6Base : 0x9000) : { + *(.G6) + } + .G7 (DEFINED(G7Base) ? G7Base : 0x40001000) : { + *(.G7) + } + .G8 (DEFINED(G8Base) ? G8Base : 0xC0000000) : { + *(.G8) + } + .G9 (DEFINED(G9Base) ? G9Base : 0) : { + *(.G9) + } + .G10 (DEFINED(G10Base) ? G10Base : 0) : { + *(.G10) + } + .G11 (DEFINED(G11Base) ? G11Base : 0) : { + *(.G11) + } + .G12 (DEFINED(G12Base) ? G12Base : 0) : { + *(.G12) + } + .G13 (DEFINED(G13Base) ? G13Base : 0) : { + *(.G13) + } + + .text TextBase : { + __virt_start = .; + __text = . ; + *(.text) + *(.fini) + . = ALIGN(16); + _isa_drivers = . ; + *(.drivisa); + _isa_drivers_end = . ; + . = ALIGN(16); + + *(.init) + _etext = . ; + /* _init = DEFINED(_init) ? _init : 0; */ + /* _fini = DEFINED(_fini) ? _fini : 0; */ + /* _argc = DEFINED(_argc) ? _argc : 0; */ + /* _argv = DEFINED(_argv) ? _argv : 0; */ + /* _envp = DEFINED(_envp) ? _envp : 0; */ + /* _hwinit = DEFINED(_hwinit) ? _hwinit : 0; */ + /* _atexit = DEFINED(_atexit) ? _atexit : 0; */ + G6Size = SIZEOF(.G6); + G7Size = SIZEOF(.G7); + G8Size = SIZEOF(.G8); + G9Size = SIZEOF(.G9); + G10Size = SIZEOF(.G10); + G11Size = SIZEOF(.G11); + G12Size = SIZEOF(.G12); + G13Size = SIZEOF(.G13); + +} + + .data SIZEOF(.text) + ADDR(.text) : { + *(.data) + _edata = . ; +} + + .bss SIZEOF(.data) + ADDR(.data) : + { + __bss_start__ = ALIGN( 0x10 ) ; + __bss = . ; + *(.bss) + *(COMMON) + __end = . ; + __bss_end__ = ALIGN( 0x10 ) ; + __ebss = . ; + } + +.eram16MB : + { + ___ramend = . - 0x7000; + } > eram16MB + + .stab 0 (NOLOAD) : + { + [ .stab ] + } + .stabstr 0 (NOLOAD) : + { + [ .stabstr ] + } + _GOT_ 0 (NOLOAD) : + { + [ _GOT_ ] + } +} diff --git a/src/arch/e1/core/longjmp.c b/src/arch/e1/core/longjmp.c new file mode 100644 index 00000000..1665f07d --- /dev/null +++ b/src/arch/e1/core/longjmp.c @@ -0,0 +1,35 @@ +/* + * Copyright 2003 Yannis Mitsos and George Thanos + * {gmitsos@gthanos}@telecom.ntua.gr + * Released under GPL2, see the file COPYING in the top directory + * + */ +#include "setjmp.h" + +unsigned long jmpbuf_ptr; + +void longjmp(jmp_buf state, int value ) +{ + if(!value) + state->__jmpbuf->ReturnValue = 1; + else + state->__jmpbuf->ReturnValue = value; + + jmpbuf_ptr = (unsigned long)state; + +#define _state_ ((struct __jmp_buf_tag*)jmpbuf_ptr) + asm volatile("mov L0, %0\n\t" + "mov L1, %1\n\t" + "mov L2, %2\n\t" + "mov G3, %3\n\t" + "mov G4, %4\n\t" + "ret PC, L1\n\t" + :/*no output*/ + :"l"(_state_->__jmpbuf->ReturnValue), + "l"(_state_->__jmpbuf->SavedPC), + "l"(_state_->__jmpbuf->SavedSR), + "l"(_state_->__jmpbuf->G3), + "l"(_state_->__jmpbuf->G4) + :"%G3", "%G4", "%L0", "%L1" ); +#undef _state_ +} diff --git a/src/arch/e1/core/memcmp.S b/src/arch/e1/core/memcmp.S new file mode 100644 index 00000000..2347317a --- /dev/null +++ b/src/arch/e1/core/memcmp.S @@ -0,0 +1,54 @@ +/* + * Derived from the Hyperstone's library source code. + * Modefied src in order to apply the -mgnu-param compiler option. + * Copyright (C) 2002-2003 GDT, Yannis Mitsos + * George Thanos + */ + .text + .align 2 + .global _memcmp + +;ENTRY (_memcmp) +_memcmp: + FRAME L9, L3 # get incoming parameters + CMPBI L2,3 # check word alignment + BNZ byte_compare + CMPBI L1,3 # check word alignment + BNZ byte_compare + +double_compare: + ADDI L0, -8 + BLT is_equal + LDD.P L1, L5 + LDD.P L2, L7 + SUB L5, L7 + DBNZ corr_8 + SUB L6, L8 + BZ double_compare + ADDI L0, 4 + ADDI L2, -4 + ADDI L1, -4 + BR byte_compare + +corr_8: ADDI L0, 8 + ADDI L2, -8 + ADDI L1, -8 +byte_compare: + ADDI L0, -1 + BLT equal + LDBU.N L2, L5, 1 # Load and compare bytes + LDBU.N L1, L6, 1 + SUB L5, L6 + BZ byte_compare + MOV L2, L5 + RET PC, L3 + +is_equal: CMPI L0, -8 + DBNE byte_compare + ADDI L0, 8 +equal: + MOVI L2, 0 + RET PC, L3 + + .END + diff --git a/src/arch/e1/core/memcpy.S b/src/arch/e1/core/memcpy.S new file mode 100644 index 00000000..0581c9d1 --- /dev/null +++ b/src/arch/e1/core/memcpy.S @@ -0,0 +1,79 @@ +/* + * Derived from the Hyperstone's library source code. + * Modefied src in order to apply the -mgnu-param compiler option. + * Copyright (C) 2002-2003 GDT, Yannis Mitsos + * George Thanos + */ + .text + .align 2 + .global _memcpy +;ENTRY(_memcpy) +_memcpy: + FRAME L8, L3 + MOV L7, L2 # Save for return + +#***************************** +# Perform byte copy if both +# not on a word alignment +#***************************** + CMPBI L2, 3 # check word alignment + BNZ mem_except + CMPBI L1, 3 # check word alignment + BNZ mem_except + +#***************************** +# Copy Double,Word,Halfword, +# then byte +#***************************** +DBL_LOOP: + CMPI L0, 8 # Copy Doubles + BLT DO_WORD + LDD.P L1, L5 + ADDI L0, -8 + DBR DBL_LOOP + STD.P L2, L5 + +DO_WORD: + CMPI L0, 4 # Copy leftover word + BLT DO_HALF + LDW.P L1, L5 + ADDI L0, -4 + DBZ DONE # Done if L0 is 0 + STW.P L2, L5 + +DO_HALF: + CMPI L0, 2 # Copy leftover byte + BLT DO_BYTE + LDHU.N L1, L5, 2 + ADDI L0, -2 + DBZ DONE # Done if L0 is 0 + STHU.N L2, L5, 2 + +DO_BYTE: + CMPI L0, 1 # Copy leftover byte + BLT DONE + LDBU.D L1, L5, 0 + STBU.D L2, L5, 0 + +DONE: # Copy done + MOV L2, L7 # Return pointer + RET PC, L3 + +#**************************** +# Byte memcpy +#**************************** +mem_except: + DBR L_5 + MOVI L6,0 +L_3: + LDBS.D L1, L5, 0 # Transfer the byte + ADDI L6, 1 + STBS.D L2, L5, 0 + ADDI L2, 1 + ADDI L1, 1 +L_5: # Loop test + CMP L6, L0 + BST L_3 + MOV L2, L7 # Return pointer + RET PC, L3 + .END diff --git a/src/arch/e1/core/memset.S b/src/arch/e1/core/memset.S new file mode 100644 index 00000000..a8ceb535 --- /dev/null +++ b/src/arch/e1/core/memset.S @@ -0,0 +1,47 @@ +/* + * Derived from the Hyperstone's library source code. + * Modefied src in order to apply the -mgnu-param compiler option. + * Copyright (C) 2002-2003 GDT, Yannis Mitsos + * George Thanos + */ + .text + .align 2 + .global _memset + +;ENTRY(_memset) +_memset: FRAME L9, L3 + MASK L5, L1, 0xFF + MOV L8, L2 + CMPI L0, 0 # if n = 0 then return + BE retour + +loop0: CMPBI L8, 0x3 + BZ word_bound + ADDI L0, -1 + DBNZ loop0 + STBU.N L8, L5, 1 +retour: RET PC, L3 + +word_bound: + CMPI L0, 8 + DBLT loop2 + MOV L7, L5 + SHLI L7, 8 + OR L5, L7 + MOV L7, L5 + SHLI L7, 16 + OR L5, L7 + MOV L6, L5 +loop1: ADDI L0, -8 + CMPI L0, 8 + DBGE loop1 + STD.P L8, L5 + CMPI L0, 0 + DBNZ loop2 + ANDNI L5, ~ 0xFF + RET PC, L3 + +loop2: ADDI L0, -1 + DBNZ loop2 + STBU.N L8, L5, 1 + RET PC, L3 diff --git a/src/arch/e1/core/setjmp.c b/src/arch/e1/core/setjmp.c new file mode 100644 index 00000000..63bcc484 --- /dev/null +++ b/src/arch/e1/core/setjmp.c @@ -0,0 +1,26 @@ +/* + * Copyright 2003 Yannis Mitsos and George Thanos + * {gmitsos@gthanos}@telecom.ntua.gr + * Released under GPL2, see the file COPYING in the top directory + * + */ +#include "setjmp.h" + +int setjmp( jmp_buf state) +{ + asm volatile( "mov %0, G3\n\t" + "mov %1, G4\n\t" + :"=l"(state->__jmpbuf->G3), + "=l"(state->__jmpbuf->G4) + :/*no input*/ + :"%G3", "%G4" ); + + asm volatile( "setadr %0\n\t" + "mov %1, L1\n\t" + "mov %2, L2\n\t" + :"=l"(state->__jmpbuf->SavedSP), + "=l"(state->__jmpbuf->SavedPC), + "=l"(state->__jmpbuf->SavedSR) + :/*no input*/); + return 0; +} diff --git a/src/arch/e1/core/start.S b/src/arch/e1/core/start.S new file mode 100644 index 00000000..691f39bd --- /dev/null +++ b/src/arch/e1/core/start.S @@ -0,0 +1,111 @@ +/* + * Derived from the Hyperstone's library source code. + * Copyright (C) 2002-2003 GDT, Yannis Mitsos + * George Thanos + */ + .global Priority ; Task-Priority + .global _Stack1Size ; Size of hardware stack + .global _Stack2Size ; Size of aggregate stack + .global _Stack1Base ; Base of hardware stack + .global _Stack2Base ; Base of aggregate stack + .global _Mem0HeapBase ; Base of Heap in Mem0 + .global _Mem1HeapBase ; Base of Heap in Mem1 + + .global _init_peripherals + .global _main + .global Main + + .global __exit + .global __fmode + .global __MaxArgCount + + .text +BasePtrs: + .weak G6Base,G7Base,G8Base,G9Base,G10Base,G11Base,G12Base,G13Base + .long G6Base,G7Base,G8Base,G9Base,G10Base,G11Base,G12Base,G13Base +BasePtrsEnd: +HeapPtrs: + .long _Mem0HeapBase + .long _Mem1HeapBase +HeapPtrsEnd: + +__MaxArgCount: + .long 32 +__StandAloneMode: + .long 0 ; 0 indicate stand alone mode + +;============================================================================; +; Startup-Code ; +;============================================================================; + .data + +; do not change the order of: __argc,.. +__argc: + .long 0 +__argv: + .long 0 +__IsShell: + .long 1 +ErrorLevel: + .long 0 +__lab: + .long 0 +__fmode: + .long 0x4000 ; O_TEXT attribute + +_isa_drivers: + .long 0 + +_isa_drivers_end: + .long 0 + + + .text +Main: +StartUp: + FRAME L5, L0 + MOVI L2, __bss_start__ ; clear the .bss segment +0: CMPI L2, __bss_end__ + BHE 0f + DBR 0b + STW.P L2, 0 +0: SUM L2, PC, BasePtrs-$ ; Load BasePtrs G6-G13 + LDD.P L2, G6 + LDD.P L2, G8 +; LDD.P L2, G10 + LDD.P L2, G12 + MOVI L2, 1 + SUM L3, PC, __StandAloneMode-$ + STW.R L3, L2 + + ;----------------------------------------------------------------; + ; Call main C function ; + ;----------------------------------------------------------------; +2: LDW.D PC, L2, __argc - $ ; pass count of arguments to main + LDW.D PC, L3, __argv - $ ; pass pointer array to main + CALL L4, PC, _init_peripherals - $ + CALL L4, PC, _main - $ ; --> Call Main-Program + CHK PC, PC ; trap if execution arrives here + +__exit: + FRAME L5, L1 + STW.D PC, L0, ErrorLevel - $ ; Store ERRORLEVEL + + CHK PC, PC + RET PC, L1 + + .global ___main +___main: + FRAME L4, L1 + MOVI L3, 2 + STW.D PC, L3, __StandAloneMode-$ + RET PC, L1 ; does not return + + .section _GOT_ + .long Main+4 ; OnCreate + .long Main+8 ; OnError + .long BasePtrs ; G6 + .long BasePtrs+4 ; G7 + .long BasePtrs+8 ; G8 + + .END diff --git a/src/arch/e1/core/strcmp.S b/src/arch/e1/core/strcmp.S new file mode 100644 index 00000000..5f09daa7 --- /dev/null +++ b/src/arch/e1/core/strcmp.S @@ -0,0 +1,76 @@ +/* + * Derived from the Hyperstone's library source code. + * Modefied src in order to apply the -mgnu-param compiler option. + * Copyright (C) 2002-2003 GDT, Yannis Mitsos + * George Thanos + */ + .text + .align 2 + .global _strcmp +;ENTRY(_strcmp) +_strcmp: + FRAME L8,L2 + CMPBI L1, 3 # check for word alignment + BNZ str_except + CMPBI L0, 3 # check for word alignment + BNZ str_except + +start: + LDD.P L1, L4 # post inc mode + LDD.P L0, L6 # post inc mode + CMPBI L4, ANYBZ + BE correct1 + CMP L4, L6 + BNE correct1 + CMP L5, L7 + BNE correct + CMPBI L5, ANYBZ + BE correct + CMPBI L6, ANYBZ + BE correct1 + CMPBI L7, ANYBZ + BNE start + +correct: MASK L4, L5, 0xff000000 + MASK L6, L7, 0xff000000 + CMP L4, L6 + BNE Exit + SHLI L5, 8 + CMPI L4, 0 + DBNE correct + SHLI L7, 8 + MOV L1, L4 + RET PC, L2 + +Exit: SUB L4, L6 # Subtract chars + SARI L4, 24 + MOV L1, L4 + RET PC, L2 + +correct1: MASK L5, L4, 0xff000000 + MASK L7, L6, 0xff000000 + CMP L5, L7 + BNE Exit1 + SHLI L4, 8 + CMPI L5, 0 + DBNE correct1 + SHLI L6, 8 + MOV L1, L5 + RET PC, L2 +Exit1: SUB L5, L7 # Subtract chars + SARI L5, 24 + MOV L1, L5 + RET PC, L2 + +testzero: CMPI L4, 0 + BE L_5 +str_except: + LDBU.N L1, L4, 1 # Load *s1, compare bytes + LDBU.N L0, L5, 1 # Load *s2, compare bytes + CMP L4, L5 + BE testzero + SUB L4, L5 # Subtract chars +L_5: MOV L1, L4 + RET PC, L2 + .END + diff --git a/src/arch/e1/include/bits/byteswap.h b/src/arch/e1/include/bits/byteswap.h new file mode 100644 index 00000000..1d1a7d2f --- /dev/null +++ b/src/arch/e1/include/bits/byteswap.h @@ -0,0 +1,39 @@ +#ifndef ETHERBOOT_BITS_BYTESWAP_H +#define ETHERBOOT_BITS_BYTESWAP_H + +/* We do not have byte swap functions ... We are + * RISC processor ... + */ + +static inline unsigned short __swap16(volatile unsigned short v) +{ + return ((v << 8) | (v >> 8)); +} + +static inline unsigned int __swap32(volatile unsigned long v) +{ + return ((v << 24) | ((v & 0xff00) << 8) | ((v & 0xff0000) >> 8) | (v >> 24)); +} + +#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_16(x) \ + (__builtin_constant_p(x) ? \ + __bswap_constant_16(x) : \ + __swap16(x)) + + +#define __bswap_32(x) \ + (__builtin_constant_p(x) ? \ + __bswap_constant_32(x) : \ + __swap32(x)) + +#endif /* ETHERBOOT_BITS_BYTESWAP_H */ diff --git a/src/arch/e1/include/bits/cpu.h b/src/arch/e1/include/bits/cpu.h new file mode 100644 index 00000000..f25c009a --- /dev/null +++ b/src/arch/e1/include/bits/cpu.h @@ -0,0 +1,6 @@ +#ifndef E1_BITS_CPU_H +#define E1_BITS_CPU_H + +#define cpu_setup() do {} while(0) + +#endif /* E1_BITS_CPU_H */ diff --git a/src/arch/e1/include/bits/elf.h b/src/arch/e1/include/bits/elf.h new file mode 100644 index 00000000..aa40e110 --- /dev/null +++ b/src/arch/e1/include/bits/elf.h @@ -0,0 +1,6 @@ +#ifndef E1_BITS_ELF_H +#define E1_BITS_ELF_H + +/* dummy file, needed for the compilation of core/nic.c */ + +#endif /* E1_BITS_ELF_H */ diff --git a/src/arch/e1/include/bits/endian.h b/src/arch/e1/include/bits/endian.h new file mode 100644 index 00000000..4145518b --- /dev/null +++ b/src/arch/e1/include/bits/endian.h @@ -0,0 +1,6 @@ +#ifndef ETHERBOOT_BITS_ENDIAN_H +#define ETHERBOOT_BITS_ENDIAN_H + +#define __BYTE_ORDER __BIG_ENDIAN + +#endif /* ETHERBOOT_BITS_ENDIAN_H */ diff --git a/src/arch/e1/include/bits/string.h b/src/arch/e1/include/bits/string.h new file mode 100644 index 00000000..b6df2fcb --- /dev/null +++ b/src/arch/e1/include/bits/string.h @@ -0,0 +1,35 @@ +#ifndef ETHERBOOT_BITS_STRING_H +#define ETHERBOOT_BITS_STRING_H + +/* define inline optimized string functions here */ + +#define __HAVE_ARCH_MEMCPY +//extern void * memcpy(const void *d, const void *s, size_t count); + +#define __HAVE_ARCH_MEMCMP +//extern int memcmp(const void * s ,const void * d ,size_t ); + +#define __HAVE_ARCH_MEMSET +//extern void * memset(const void * s, int c, size_t count); + +#define __HAVE_ARCH_MEMMOVE +static inline void *memmove(void *s1, const void *s2, size_t n) { + + unsigned int i; + char *tmp = s1; + char *cs2 = (char *) s2; + + if (tmp < cs2) { + for(i=0; i> 8)); +} + +static inline unsigned int _swapl(volatile unsigned long v) +{ + return ((v << 24) | ((v & 0xff00) << 8) | ((v & 0xff0000) >> 8) | (v >> 24)); +} + +#define hy_inpw(addr) \ + ({ register unsigned long dummy, dummy1; \ + dummy = addr; \ + asm volatile ("LDW.IOD %1, %0, 0" \ + : "=l" (dummy1) \ + : "l" (dummy)); dummy1; }) + + +#define hy_outpw(x, addr) \ + ({ register unsigned long dummy0,dummy1; \ + dummy0 = addr; \ + dummy1 = x; \ + asm volatile ("STW.IOD %1, %0, 0" \ + : "=l" (dummy1) \ + : "l"(dummy0), "l" (dummy1)); dummy1; }) + +#define readb(addr) ({ unsigned char __v = inregb(addr); __v; }) +#define readw(addr) ({ unsigned short __v = inregw(addr); __v; }) +#define readl(addr) ({ unsigned long __v = inregl(addr); __v; }) + +#define writeb(b,addr) (void)(outreg(b, addr)) +#define writew(b,addr) (void)(outreg(b, addr)) +#define writel(b,addr) (void)(outreg(b, addr)) + +static inline unsigned long common_io_access(unsigned long addr) +{ + return io_periph[(addr & 0x03C00000) >> 22]; +} + +static inline volatile unsigned char inregb(volatile unsigned long reg) +{ + unsigned char val; + + val = hy_inpw(common_io_access(reg) | ((0xf & reg) << IORegAddress)); + return val; +} + +static inline volatile unsigned short inregw(volatile unsigned long reg) +{ + unsigned short val; + + val = hy_inpw(common_io_access(reg) | ((0xf & reg) << IORegAddress)); + return val; +} + +static inline volatile unsigned long inregl(volatile unsigned long reg) +{ + unsigned long val; + + val = hy_inpw(common_io_access(reg) | ((0xf & reg) << IORegAddress)); + return val; +} + +static inline void outreg(volatile unsigned long val, volatile unsigned long reg) +{ + + hy_outpw(val, (common_io_access(reg) | ((0xf & reg) << IORegAddress))); +} + +static inline void io_outsb(unsigned int addr, void *buf, int len) +{ + unsigned long tmp; + unsigned char *bp = (unsigned char *) buf; + + tmp = (common_io_access(addr)) | ((0xf & addr) << IORegAddress); + + while (len--){ + hy_outpw(_swapw(*bp++), tmp); + } +} + +static inline void io_outsw(volatile unsigned int addr, void *buf, int len) +{ + unsigned long tmp; + unsigned short *bp = (unsigned short *) buf; + + tmp = (common_io_access(addr)) | ((0xf & addr) << IORegAddress); + + while (len--){ + hy_outpw(_swapw(*bp++), tmp); + } +} + +static inline void io_outsl(volatile unsigned int addr, void *buf, int len) +{ + unsigned long tmp; + unsigned int *bp = (unsigned int *) buf; + + tmp = (common_io_access(addr)) | ((0xf & addr) << IORegAddress); + + while (len--){ + hy_outpw(_swapl(*bp++), tmp); + } +} + +static inline void io_insb(volatile unsigned int addr, void *buf, int len) +{ + unsigned long tmp; + unsigned char *bp = (unsigned char *) buf; + + tmp = (common_io_access(addr)) | ((0xf & addr) << IORegAddress); + + while (len--) + *bp++ = hy_inpw((unsigned char) tmp); + +} + +static inline void io_insw(unsigned int addr, void *buf, int len) +{ + unsigned long tmp; + unsigned short *bp = (unsigned short *) buf; + + tmp = (common_io_access(addr)) | ((0xf & addr) << IORegAddress); + + while (len--) + *bp++ = _swapw((unsigned short)hy_inpw(tmp)); + +} + +static inline void io_insl(unsigned int addr, void *buf, int len) +{ + unsigned long tmp; + unsigned int *bp = (unsigned int *) buf; + + tmp = (common_io_access(addr)) | ((0xf & addr) << IORegAddress); + + while (len--) + *bp++ = _swapl((unsigned int)hy_inpw(tmp)); +} + +#define inb(addr) readb(addr) +#define inw(addr) readw(addr) +#define inl(addr) readl(addr) +#define outb(x,addr) ((void) writeb(x,addr)) +#define outw(x,addr) ((void) writew(x,addr)) +#define outl(x,addr) ((void) writel(x,addr)) + +#define insb(a,b,l) io_insb(a,b,l) +#define insw(a,b,l) io_insw(a,b,l) +#define insl(a,b,l) io_insl(a,b,l) +#define outsb(a,b,l) io_outsb(a,b,l) +#define outsw(a,b,l) io_outsw(a,b,l) +#define outsl(a,b,l) io_outsl(a,b,l) + +#define memcpy_fromio(a,b,c) memcpy((a),(void *)(b),(c)) +#define memcpy_toio(a,b,c) memcpy((void *)(a),(b),(c)) + +#endif /* ETHERBOOT_IO_H */ diff --git a/src/arch/e1/include/latch.h b/src/arch/e1/include/latch.h new file mode 100644 index 00000000..0ee6fb2a --- /dev/null +++ b/src/arch/e1/include/latch.h @@ -0,0 +1,12 @@ +#ifndef LATCH_H +#define LATCH_H + +//#define TICKS_PER_SEC (1000000UL) +#define TICKS_PER_SEC (625000UL) + +/* Fixed timer interval used for calibrating a more precise timer */ +//#define LATCHES_PER_SEC 10 + +void sleep_latch(void); + +#endif /* LATCH_H */ diff --git a/src/arch/e1/include/limits.h b/src/arch/e1/include/limits.h new file mode 100644 index 00000000..e7056ce5 --- /dev/null +++ b/src/arch/e1/include/limits.h @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------*/ +/* Project: ANSI C Standard Header Files */ +/* File: LIMITS.H */ +/* Edited by: hyperstone electronics GmbH */ +/* Am Seerhein 8 */ +/* D-78467 Konstanz, Germany */ +/* Date: January 30, 1996 */ +/*--------------------------------------------------------------------------*/ +/* Purpose: */ +/* The header file defines limits of ordinal types */ +/* (char, short, int, long) */ +/*--------------------------------------------------------------------------*/ + +#ifndef __LIMITS_H +#define __LIMITS_H 1 + +#define MB_LEN_MAX 1 +#define CHAR_BIT 8 +#define SCHAR_MIN -128L +#define SCHAR_MAX 127L +#define UCHAR_MAX 255 +#define CHAR_MIN 0 +#define CHAR_MAX UCHAR_MAX +#define SHRT_MIN -32768 +#define SHRT_MAX 32767 +#define USHRT_MAX 65535 +#define INT_MIN 0x80000000 +#define INT_MAX 0x7FFFFFFF +#define UINT_MAX 0xFFFFFFFFL +#define LONG_MIN INT_MIN +#define LONG_MAX INT_MAX +#define ULONG_MAX UINT_MAX + +#endif diff --git a/src/arch/e1/include/setjmp.h b/src/arch/e1/include/setjmp.h new file mode 100644 index 00000000..ef401b62 --- /dev/null +++ b/src/arch/e1/include/setjmp.h @@ -0,0 +1,23 @@ +#ifndef _SETJMP_H +#define _SETJMP_H + + +typedef struct { + unsigned long G3; + unsigned long G4; + unsigned long SavedSP; + unsigned long SavedPC; + unsigned long SavedSR; + unsigned long ReturnValue; +} __jmp_buf[1]; + +typedef struct __jmp_buf_tag /* C++ doesn't like tagless structs. */ + { + __jmp_buf __jmpbuf; /* Calling environment. */ + int __mask_was_saved; /* Saved the signal mask? */ + } jmp_buf[1]; + +void longjmp(jmp_buf state, int value ); +int setjmp( jmp_buf state); + +#endif diff --git a/src/arch/e1/include/stdint.h b/src/arch/e1/include/stdint.h new file mode 100644 index 00000000..8a7ad978 --- /dev/null +++ b/src/arch/e1/include/stdint.h @@ -0,0 +1,16 @@ +#ifndef STDINT_H +#define STDINT_H + +typedef unsigned long size_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 int int32_t; +typedef signed long long int64_t; + +#endif /* STDINT_H */ diff --git a/src/arch/i386/Config b/src/arch/i386/Config new file mode 100644 index 00000000..e8ac91b8 --- /dev/null +++ b/src/arch/i386/Config @@ -0,0 +1,131 @@ +# 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. +# -DIBM_L40 +# This option uses the 0x92 method of controlling +# A20 instead of the traditional method of using the +# keyboard controller. An explanation of A20 is here: +# http://www.win.tue.nl/~aeb/linux/kbd/A20.html +# This occurs on MCA, EISA and some embedded boards, +# and sometimes with the Fast Gate A20 option on some +# BIOSes. +# Enable this only if you are sure of what you are doing. +# +# 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 inside the Bochs emulator. The pnic +# driver is probably the best one to try. +# CFLAGS+= -DCONFIG_PCI_DIRECT + +# 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 +GCC_MINORVERSION = $(word 2, $(GCC_VERSION)) +ifneq ($(GCC_MINORVERSION),4) +CFLAGS+= -mcpu=i386 +endif + +LDFLAGS+= -N + +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/src/arch/i386/Makefile b/src/arch/i386/Makefile new file mode 100644 index 00000000..cded46c3 --- /dev/null +++ b/src/arch/i386/Makefile @@ -0,0 +1,373 @@ +ARCH_FORMAT= elf32-i386 + +# For debugging, don't delete intermediates +#.SECONDARY: + +LDSCRIPT= arch/i386/core/etherboot.lds +PLDSCRIPT= arch/i386/core/etherboot.prefix.lds + +LCONFIG+= -Ui386 + +ROMLIMIT= 524288 +CHECKSIZE= { read d1; read d1 d2 d3 size d4; [ $$size -gt $(ROMLIMIT) ] &&\ + { $(RM) $@; echo "ERROR: code size exceeds limit!"; exit 1; }; exit 0; } + +START= $(BIN)/start32.o $(BIN)/linuxbios.o \ + $(BIN)/bios.o $(BIN)/console.o $(BIN)/memsizes.o $(BIN)/basemem.o \ + $(BIN)/hidemem.o $(BIN)/e820mangler.o \ + $(BIN)/realmode.o $(BIN)/realmode_asm.o \ + $(BIN)/callbacks.o $(BIN)/pxe_callbacks.o + +SRCS+= arch/i386/prefix/floppyprefix.S +SRCS+= arch/i386/prefix/unhuf.S +SRCS+= arch/i386/prefix/unnrv2b.S +SRCS+= arch/i386/firmware/pcbios/bios.c +SRCS+= arch/i386/firmware/pcbios/console.c +SRCS+= arch/i386/firmware/pcbios/memsizes.c +SRCS+= arch/i386/firmware/pcbios/basemem.c +SRCS+= arch/i386/firmware/pcbios/hidemem.c +SRCS+= arch/i386/firmware/pcbios/e820mangler.S +SRCS+= arch/i386/prefix/liloprefix.S +SRCS+= arch/i386/prefix/elfprefix.S +SRCS+= arch/i386/prefix/lmelf_prefix.S +SRCS+= arch/i386/prefix/elf_dprefix.S +SRCS+= arch/i386/prefix/lmelf_dprefix.S +SRCS+= arch/i386/prefix/comprefix.S +SRCS+= arch/i386/prefix/exeprefix.S +SRCS+= arch/i386/prefix/pxeprefix.S +SRCS+= arch/i386/prefix/romprefix.S + +SRCS+= arch/i386/core/init.S +SRCS+= arch/i386/core/start32.S +SRCS+= arch/i386/core/pci_io.c +SRCS+= arch/i386/core/i386_timer.c +SRCS+= arch/i386/core/elf.c +SRCS+= arch/i386/core/cpu.c +SRCS+= arch/i386/core/video_subr.c +SRCS+= arch/i386/core/pic8259.c +SRCS+= arch/i386/core/hooks.c +SRCS+= arch/i386/core/callbacks.c +SRCS+= arch/i386/core/realmode.c +SRCS+= arch/i386/core/realmode_asm.S +SRCS+= arch/i386/core/pxe_callbacks.c + +# ROM loaders: ISA and PCI versions +ISAPREFIX= $(BIN)/isaprefix.o +ISAENTRY= $(BIN)/isaprefix.entry.o +ISAEXIT= $(BIN)/isaprefix.exit.o +PCIPREFIX= $(BIN)/pciprefix.o +PCIENTRY= $(BIN)/pciprefix.entry.o +PCIEXIT= $(BIN)/pciprefix.exit.o +# Variables xxx_ROMTYPE are defined by genrules.pl. ROMENTRY and +# ROMEXIT will evaluate to give the correct objects to use. +TARGETBASE=$(patsubst $(BIN)/%,%,$(firstword $(subst ., ,$@))) +ROMCARD=$(firstword $(subst --, ,$(TARGETBASE))) +ROMTYPE=$(firstword $(ROMTYPE_$(ROMCARD)) ISA) +romENTRY=$($(ROMTYPE)ENTRY) +romEXIT=$($(ROMTYPE)EXIT) + +# Target type for generic prf rules +TARGETTYPE=$(patsubst .%,%, $(suffix $(basename $@))) +TARGETENTRY=$($(TARGETTYPE)ENTRY) +TARGETEXIT=$($(TARGETTYPE)EXIT) + +# Other real-mode entry loaders +dskPREFIX= $(BIN)/floppyprefix.o +dskENTRY= $(BIN)/floppyprefix.entry.o +dskEXIT= $(BIN)/floppyprefix.exit.o +comPREFIX= $(BIN)/comprefix.o +comENTRY= $(BIN)/comprefix.entry.o +comEXIT= $(BIN)/comprefix.exit.o +exePREFIX= $(BIN)/exeprefix.o +exeENTRY= $(BIN)/exeprefix.entry.o +exeEXIT= $(BIN)/exeprefix.exit.o +liloPREFIX= $(BIN)/liloprefix.o +liloENTRY= $(BIN)/liloprefix.entry.o +liloEXIT= $(BIN)/liloprefix.exit.o +bImagePREFIX= $(BIN)/bImageprefix.o +bImageENTRY= $(BIN)/bImageprefix.entry.o +bImageEXIT= $(BIN)/bImageprefix.exit.o +pxePREFIX= $(BIN)/pxeprefix.o +pxeENTRY= $(BIN)/pxeprefix.entry.o +pxeEXIT= $(BIN)/pxeprefix.exit.o +rawPREFIX= $(BIN)/nullprefix.o +rawENTRY= $(BIN)/nullprefix.entry.o +rawEXIT= $(BIN)/nullprefix.exit.o + +# Protected mode entry loaders +elfPREFIX= $(BIN)/elfprefix.o +elfENTRY= $(BIN)/elfprefix.entry.o +elfEXIT= $(BIN)/elfprefix.exit.o +lmelfPREFIX= $(BIN)/lmelf_prefix.o +lmelfENTRY= $(BIN)/lmelf_prefix.entry.o +lmelfEXIT= $(BIN)/lmelf_prefix.exit.o +elfdPREFIX= $(BIN)/elf_dprefix.o +elfdENTRY= $(BIN)/elf_dprefix.entry.o +elfdEXIT= $(BIN)/elf_dprefix.exit.o +lmelfdPREFIX= $(BIN)/lmelf_dprefix.o +lmelfdENTRY= $(BIN)/lmelf_dprefix.entry.o +lmelfdEXIT= $(BIN)/lmelf_dprefix.exit.o + +include $(BIN)/Roms + +all: $(ROMS) +allroms: $(ROMS) +allzroms: $(ROMS) +alldsks: $(EB_DSKS) +allzdsks: $(EB_ZDSKS) +alllilos: $(EB_LILOS) +allzlilos: $(EB_ZLILOS) +allbImages: $(EB_BIMAGES) +allbzImages: $(EB_BZIMAGES) +allpxes: $(EB_PXES) +allzpxes: $(EB_ZPXES) +allelfs: $(EB_ELFS) +allzelfs: $(EB_ZELFS) +alllmelfs: $(EB_LMELFS) +allzlmelfs: $(EB_ZLMELFS) +allelfds: $(EB_ELFDS) +allzelfds: $(EB_ZELFDS) +alllmelfds: $(EB_LMELFDS) +allzlmelfds: $(EB_ZLMELFDS) +allcoms: $(EB_COMS) +allexes: $(EB_EXES) +allisos: $(EB_ISOS) +alllisos: $(EB_LISOS) + +BOBJS+= $(BIN)/pci_io.o $(BIN)/i386_timer.o +BOBJS+= $(BIN)/elf.o $(BIN)/cpu.o $(BIN)/video_subr.o +BOBJS+= $(BIN)/pic8259.o $(BIN)/hooks.o + +# ROM loaders + +$(ISAPREFIX): arch/i386/prefix/romprefix.S $(MAKEDEPS) + $(CPP) $(CFLAGS) $(LCONFIG) -Ui386 -D ASSEMBLY $< \ + | $(AS) $(ASFLAGS) -o $@ + +$(PCIPREFIX): arch/i386/prefix/romprefix.S $(MAKEDEPS) + $(CPP) -DPCI_PNP_HEADER $(CFLAGS) $(LCONFIG) -Ui386 -D ASSEMBLY $< \ + | $(AS) $(ASFLAGS) -o $@ + +# Prefix splitters +$(BIN)/%prefix.entry.o: $(BIN)/%prefix.o $(MAKEDEPS) + $(OBJCOPY) -R .text16 $< $@ + +$(BIN)/%prefix.exit.o: $(BIN)/%prefix.o $(MAKEDEPS) + $(OBJCOPY) -R .prefix $< $@ + +# Generic prefix objects +PREFIXOBJS = $(BIN)/init.o +ZPREFIXOBJS = $(BIN)/init.o $(BIN)/unnrv2b.o + +# Utilities +$(BIN)/nrv2b: util/nrv2b.c + $(HOST_CC) -O2 -DENCODE -DDECODE -DMAIN -DVERBOSE -DNDEBUG -DBITSIZE=32 -DENDIAN=0 -o $@ $< + +ZFILELEN = perl util/zfilelen.pl + +# Pattern Rules + +# General for compiling/assembly source files + +$(BIN)/%.o: arch/i386/core/%.c $(MAKEDEPS) + $(CC) $(CFLAGS) -o $@ -c $< + +$(BIN)/%.o: arch/i386/core/%.S $(MAKEDEPS) + $(CPP) $(CFLAGS) -Ui386 -D ASSEMBLY $< | $(AS) $(ASFLAGS) -o $@ + +$(BIN)/%.o: arch/i386/firmware/pcbios/%.c $(MAKEDEPS) + $(CC) $(CFLAGS) -o $@ -c $< + +$(BIN)/%.o: arch/i386/firmware/pcbios/%.S $(MAKEDEPS) + $(CPP) $(CFLAGS) -Ui386 -D ASSEMBLY $< | $(AS) $(ASFLAGS) -o $@ + +$(BIN)/%.o: arch/i386/prefix/%.S $(MAKEDEPS) + $(CPP) $(CFLAGS) -Ui386 -D ASSEMBLY $< | $(AS) $(ASFLAGS) -o $@ + +# general rule for 16bit .o, may be overridden +$(BIN)/%.o: $(BIN)/%.s + $(AS) $(ASFLAGS) -o $@ $< + +# general rule for .bin (plain binary loader code), may be overridden +$(BIN)/%.bin: $(BIN)/%.o + $(OBJCOPY) -O binary $< $@ + +# general rule for .z (compressed binary code), may be overridden +# rule for .z is in top level Makefile +# Give the directory name, e.g. use $(BIN)/rtl8139.com as the target. + +$(BIN)/%.zo: $(BIN)/%.zbin arch/i386/core/prefixzdata.lds $(MAKEDEPS) + $(LD) -T arch/i386/core/prefixzdata.lds -b binary $< -o $@ + +$(BIN)/%.uo: $(BIN)/%.bin arch/i386/core/prefixudata.lds $(MAKEDEPS) + $(LD) -T arch/i386/core/prefixudata.lds -b binary $< -o $@ + +# Intermediate prf rules + +%.prf: %.rt $(PREFIXOBJS) %.rt1.uo %.rt2.uo $(MAKEDEPS) + $(MAKE) $(TARGETENTRY) + $(LD) $(LDFLAGS) -T $(PLDSCRIPT) $(TARGETENTRY) -R $(subst $(MAKEDEPS),,$^) -o $@ + +%.zprf: %.rt $(ZPREFIXOBJS) %.rt1.uo %.rt2.zo $(MAKEDEPS) + $(MAKE) $(TARGETENTRY) + $(LD) $(LDFLAGS) -T $(PLDSCRIPT) $(TARGETENTRY) -R $(subst $(MAKEDEPS),,$^) -o $@ + +# general rules for normal/compressed ROM images, may be overridden +SUFFIXES += rom zrom + +$(BIN)/%.rom.rt: $(BIN)/%.rt.o $(ISAENTRY) $(PCIENTRY) $(ISAEXIT) $(PCIEXIT) $(LDSCRIPT) $(MAKEDEPS) + $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $(romEXIT) $< + @$(SIZE) $@ | $(CHECKSIZE) + +$(BIN)/%.rom: $(BIN)/%.rom.prf + $(OBJCOPY) -O binary $< $@ + $(MAKEROM) $(MAKEROM_FLAGS) $(MAKEROM_$(ROMCARD)) $(MAKEROM_ID_$(ROMCARD)) -i$(IDENT) $@ + +$(BIN)/%.zrom: $(BIN)/%.rom.zprf + $(OBJCOPY) -O binary $< $@ + $(MAKEROM) $(MAKEROM_FLAGS) $(MAKEROM_$(ROMCARD)) $(MAKEROM_ID_$(ROMCARD)) -i$(IDENT) $@ + +# general rules for ELF images +SUFFIXES += elf zelf +$(BIN)/%.elf.rt: $(BIN)/%.rt.o $(elfENTRY) $(elfEXIT) $(LDSCRIPT) $(MAKEDEPS) + $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $(elfEXIT) $< + +$(BIN)/%.elf: $(BIN)/%.elf.prf + $(OBJCOPY) -O binary $< $@ + +$(BIN)/%.zelf: $(BIN)/%.elf.zprf + $(OBJCOPY) -O binary $< $@ + +# general rules for Long Mode ELF images +SUFFIXES += lmelf zlmelf +$(BIN)/%.lmelf.rt: $(BIN)/%.rt.o $(lmelfENTRY) $(lmelfEXIT) $(LDSCRIPT) $(MAKEDEPS) + $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $(lmelfEXIT) $< + +$(BIN)/%.lmelf: $(BIN)/%.lmelf.prf + $(OBJCOPY) -O binary $< $@ + +$(BIN)/%.zlmelf: $(BIN)/%.lmelf.zprf + $(OBJCOPY) -O binary $< $@ + +# general rules for ELF dynamic images +SUFFIXES += elfd zelfd +$(BIN)/%.elfd.rt: $(BIN)/%.rt.o $(elfdENTRY) $(elfdEXIT) $(LDSCRIPT) $(MAKEDEPS) + $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $(elfdEXIT) $< + +$(BIN)/%.elfd: $(BIN)/%.elfd.prf + $(OBJCOPY) -O binary $< $@ + +$(BIN)/%.zelfd: $(BIN)/%.elfd.zprf + $(OBJCOPY) -O binary $< $@ + +# general rules for Long Mode ELF dynamic images +SUFFIXES += lmelfd zlmelfd +$(BIN)/%.lmelfd.rt: $(BIN)/%.rt.o $(lmelfdENTRY) $(lmelfdEXIT) $(LDSCRIPT) $(MAKEDEPS) + $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $(lmelfdEXIT) $< + +$(BIN)/%.lmelfd: $(BIN)/%.lmelfd.prf + $(OBJCOPY) -O binary $< $@ + +$(BIN)/%.zlmelfd: $(BIN)/%.lmelfd.zprf + $(OBJCOPY) -O binary $< $@ + +# rules to generate a DOS loadable .com executable +SUFFIXES += com +$(BIN)/%.com.rt: $(BIN)/%.rt.o $(comENTRY) $(comEXIT) $(LDSCRIPT) $(MAKEDEPS) + $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $< $(comEXIT) + +$(BIN)/%.com: $(BIN)/%.com.zprf + $(OBJCOPY) -O binary $< $@ + +# rules to generate a DOS loadable .exe executable +SUFFIXES += exe +$(BIN)/%.exe.rt: $(BIN)/%.rt.o $(exeENTRY) $(exeEXIT) $(LDSCRIPT) $(MAKEDEPS) + $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $< $(exeEXIT) + @$(SIZE) $@ | $(CHECKSIZE) + +$(BIN)/%.exe: $(BIN)/%.exe.prf + $(OBJCOPY) -O binary $< $@ + +# rules to make a LILO loadable image +SUFFIXES += lilo zlilo + +$(BIN)/%.lilo.rt: $(BIN)/%.rt.o $(liloENTRY) $(liloEXIT) $(LDSCRIPT) $(MAKEDEPS) + $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $< $(liloEXIT) + @$(SIZE) $@ | $(CHECKSIZE) + +$(BIN)/%.lilo: $(BIN)/%.lilo.prf + $(OBJCOPY) -O binary $< $@ + +$(BIN)/%.zlilo: $(BIN)/%.lilo.zprf + $(OBJCOPY) -O binary $< $@ + +# rules to make big linux boot protocol image +SUFFIXES += bImage bzImage + +$(BIN)/%.bImage.rt: $(BIN)/%.rt.o $(bImageENTRY) $(bImageEXIT) $(LDSCRIPT) $(MAKEDEPS) + $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $< $(bImageEXIT) + +$(BIN)/%.bImage: $(BIN)/%.bImage.prf + $(OBJCOPY) -O binary $< $@ + +$(BIN)/%.bzImage: $(BIN)/%.bImage.zprf + $(OBJCOPY) -O binary $< $@ + + +# rules to generate a PXE loadable image +SUFFIXES += pxe zpxe + +$(BIN)/%.pxe.rt: $(BIN)/%.rt.o $(pxeENTRY) $(pxeEXIT) $(LDSCRIPT) $(MAKEDEPS) + $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $< $(pxeEXIT) + @$(SIZE) $@ | $(CHECKSIZE) + +$(BIN)/%.pxe: $(BIN)/%.pxe.prf + $(OBJCOPY) -O binary $< $@ + +$(BIN)/%.zpxe: $(BIN)/%.pxe.zprf + $(OBJCOPY) -O binary $< $@ + +# rules to generate the .dsk/.zdsk floppy images +SUFFIXES += dsk zdsk + +$(BIN)/%.dsk.rt: $(BIN)/%.rt.o $(dskENTRY) $(dskEXIT) $(LDSCRIPT) $(MAKEDEPS) + $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $< $(dskEXIT) + @$(SIZE) $@ | $(CHECKSIZE) + +$(BIN)/%.dsk: $(BIN)/%.dsk.prf + $(OBJCOPY) -O binary $< $@ + +$(BIN)/%.zdsk: $(BIN)/%.dsk.zprf + $(OBJCOPY) -O binary $< $@ + +# rules to write the .dsk/.zdsk image onto a blank floppy +SUFFIXES += fd0 zfd0 +%.fd0: %.dsk + dd if=$< bs=512 conv=sync of=/dev/fd0 + sync + +%.zfd0: %.zdsk + dd if=$< bs=512 conv=sync of=/dev/fd0 + sync + +# rules to create raw executable images +SUFFIXES += raw zraw +$(BIN)/%.raw.rt: $(BIN)/%.rt.o $(rawENTRY) $(rawEXIT) $(LDSCRIPT) $(MAKEDEPS) + $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $< $(rawEXIT) + +$(BIN)/%.raw: $(BIN)/%.raw.prf + $(OBJCOPY) -O binary $< $@ + +$(BIN)/%.zraw: $(BIN)/%.raw.zprf + $(OBJCOPY) -O binary $< $@ + +# rule to make a non-emulation ISO boot image +SUFFIXES += iso +%.iso: util/geniso %.zlilo + ISOLINUX_BIN=${ISOLINUX_BIN} bash util/geniso $*.iso $*.zlilo + +# rule to make a floppy emulation ISO boot image +SUFFIXES += liso +%.liso: util/genliso %.zlilo + bash util/genliso $*.liso $*.zlilo + diff --git a/src/arch/i386/core/aout_loader.c b/src/arch/i386/core/aout_loader.c new file mode 100644 index 00000000..f85620e9 --- /dev/null +++ b/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/src/arch/i386/core/callbacks.c b/src/arch/i386/core/callbacks.c new file mode 100644 index 00000000..d45e63e2 --- /dev/null +++ b/src/arch/i386/core/callbacks.c @@ -0,0 +1,107 @@ +/* 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 , January 2004. + */ + +#include "etherboot.h" +#include "callbacks.h" +#include "realmode.h" +#include "segoff.h" +#include + +/* Maximum amount of stack data that prefix may request to be passed + * to its exit routine + */ +#define MAX_PREFIX_STACK_DATA 16 + +/* Prefix exit routine is defined in prefix object */ +extern void prefix_exit ( void ); +extern void prefix_exit_end ( void ); + +/***************************************************************************** + * + * IN_CALL INTERFACE + * + ***************************************************************************** + */ + +/* in_call(): entry point for calls in to Etherboot from external code. + * + * Parameters: some set up by assembly code _in_call(), others as + * passed from external code. + */ +uint32_t i386_in_call ( va_list ap, i386_pm_in_call_data_t pm_data, + uint32_t opcode ) { + uint32_t ret; + i386_rm_in_call_data_t rm_data; + in_call_data_t in_call_data = { &pm_data, NULL }; + struct { + int data[MAX_PREFIX_STACK_DATA/4]; + } in_stack; + + /* Fill out rm_data if we were called from real mode */ + if ( opcode & EB_CALL_FROM_REAL_MODE ) { + in_call_data.rm = &rm_data; + rm_data = va_arg ( ap, typeof(rm_data) ); + /* Null return address indicates to use the special + * prefix exit mechanism, and that there are + * parameters on the stack that the prefix wants + * handed to its exit routine. + */ + if ( rm_data.ret_addr.offset == 0 ) { + int n = va_arg ( ap, int ) / 4; + int i; + for ( i = 0; i < n; i++ ) { + in_stack.data[i] = va_arg ( ap, int ); + } + } + } + + /* Hand off to main in_call() routine */ + ret = in_call ( &in_call_data, opcode, ap ); + + /* If real-mode return address is null, it means that we + * should exit via the prefix's exit path, which is part of + * our image. (This arrangement is necessary since the prefix + * code itself may have been vapourised by the time we want to + * return.) + */ + if ( ( opcode & EB_CALL_FROM_REAL_MODE ) && + ( rm_data.ret_addr.offset == 0 ) ) { + real_call ( prefix_exit, &in_stack, NULL ); + /* Should never return */ + } + + return ret; +} + +#ifdef CODE16 + +/* install_rm_callback_interface(): install real-mode callback + * interface at specified address. + * + * Real-mode code may then call to this address (or lcall to this + * address plus RM_IN_CALL_FAR) in order to make an in_call() to + * Etherboot. + * + * Returns the size of the installed code, or 0 if the code could not + * be installed. + */ +int install_rm_callback_interface ( void *address, size_t available ) { + if ( available && + ( available < rm_callback_interface_size ) ) return 0; + + /* Inform RM code where to find Etherboot */ + rm_etherboot_location = virt_to_phys(_text); + + /* Install callback interface */ + memcpy ( address, &rm_callback_interface, + rm_callback_interface_size ); + + return rm_callback_interface_size; +} + +#endif /* CODE16 */ diff --git a/src/arch/i386/core/cpu.c b/src/arch/i386/core/cpu.c new file mode 100644 index 00000000..8a0f7333 --- /dev/null +++ b/src/arch/i386/core/cpu.c @@ -0,0 +1,86 @@ +#ifdef CONFIG_X86_64 +#include "stdint.h" +#include "string.h" +#include "bits/cpu.h" + + +/* Standard macro to see if a specific flag is changeable */ +static inline int flag_is_changeable_p(uint32_t 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; +} + + +/* Probe for the CPUID instruction */ +static inline int have_cpuid_p(void) +{ + return flag_is_changeable_p(X86_EFLAGS_ID); +} + +static void identify_cpu(struct cpuinfo_x86 *c) +{ + unsigned xlvl; + + c->cpuid_level = -1; /* CPUID not detected */ + c->x86_model = c->x86_mask = 0; /* So far unknown... */ + c->x86_vendor_id[0] = '\0'; /* Unset */ + memset(&c->x86_capability, 0, sizeof c->x86_capability); + + if (!have_cpuid_p()) { + /* CPU doesn'thave CPUID */ + + /* If there are any capabilities, they'r vendor-specific */ + /* enable_cpuid() would have set c->x86 for us. */ + } + else { + /* CPU does have CPUID */ + + /* Get vendor name */ + cpuid(0x00000000, &c->cpuid_level, + (int *)&c->x86_vendor_id[0], + (int *)&c->x86_vendor_id[8], + (int *)&c->x86_vendor_id[4]); + + /* Initialize the standard set of capabilities */ + /* Note that the vendor-specific code below might override */ + + /* Intel-defined flags: level 0x00000001 */ + if ( c->cpuid_level >= 0x00000001 ) { + unsigned tfms, junk; + cpuid(0x00000001, &tfms, &junk, &junk, + &c->x86_capability[0]); + c->x86 = (tfms >> 8) & 15; + c->x86_model = (tfms >> 4) & 15; + c->x86_mask = tfms & 15; + } + + /* AMD-defined flags: level 0x80000001 */ + xlvl = cpuid_eax(0x80000000); + if ( (xlvl & 0xffff0000) == 0x80000000 ) { + if ( xlvl >= 0x80000001 ) + c->x86_capability[1] = cpuid_edx(0x80000001); + } + } +} + +struct cpuinfo_x86 cpu_info; +void cpu_setup(void) +{ + identify_cpu(&cpu_info); +} +#endif /* CONFIG_X86_64 */ diff --git a/src/arch/i386/core/elf.c b/src/arch/i386/core/elf.c new file mode 100644 index 00000000..6ef78c71 --- /dev/null +++ b/src/arch/i386/core/elf.c @@ -0,0 +1,135 @@ +#include "etherboot.h" +#include "elf.h" + + +#define NAME "Etherboot" + +#if defined(PCBIOS) +#define FIRMWARE "PCBIOS" +#endif +#if defined(LINUXBIOS) +#define FIRMWARE "LinuxBIOS" +#endif +#if !defined(FIRMWARE) +#error "No BIOS selected" +#endif + +#define SZ(X) ((sizeof(X)+3) & ~3) +#define CP(D,S) (memcpy(&(D), &(S), sizeof(S))) + +struct elf_notes { + /* The note header */ + struct Elf_Bhdr hdr; + + /* First the Fixed sized entries that must be well aligned */ + + /* Pointer to bootp data */ + Elf_Nhdr nf1; + char nf1_name[SZ(EB_PARAM_NOTE)]; + uint32_t nf1_bootp_data; + + /* Pointer to ELF header */ + Elf_Nhdr nf2; + char nf2_name[SZ(EB_PARAM_NOTE)]; + uint32_t nf2_header; + + /* A copy of the i386 memory map */ + Elf_Nhdr nf3; + char nf3_name[SZ(EB_PARAM_NOTE)]; + struct meminfo nf3_meminfo; + + /* Then the variable sized data string data where alignment does not matter */ + + /* The bootloader name */ + Elf_Nhdr nv1; + char nv1_desc[SZ(NAME)]; + /* The bootloader version */ + Elf_Nhdr nv2; + char nv2_desc[SZ(VERSION)]; + /* The firmware type */ + Elf_Nhdr nv3; + char nv3_desc[SZ(FIRMWARE)]; + /* Name of the loaded image */ + Elf_Nhdr nv4; + char nv4_loaded_image[128]; + /* An empty command line */ + Elf_Nhdr nv5; + char nv5_cmdline[SZ("")]; +}; + +#define ELF_NOTE_COUNT (3 + 5) + +static struct elf_notes notes; +struct Elf_Bhdr *prepare_boot_params(void *header) +{ + memset(¬es, 0, sizeof(notes)); + notes.hdr.b_signature = ELF_BHDR_MAGIC; + notes.hdr.b_size = sizeof(notes); + notes.hdr.b_checksum = 0; + notes.hdr.b_records = ELF_NOTE_COUNT; + + /* Initialize the fixed length entries. */ + notes.nf1.n_namesz = sizeof(EB_PARAM_NOTE); + notes.nf1.n_descsz = sizeof(notes.nf1_bootp_data); + notes.nf1.n_type = EB_BOOTP_DATA; + CP(notes.nf1_name, EB_PARAM_NOTE); + notes.nf1_bootp_data = virt_to_phys(BOOTP_DATA_ADDR); + + notes.nf2.n_namesz = sizeof(EB_PARAM_NOTE); + notes.nf2.n_descsz = sizeof(notes.nf2_header); + notes.nf2.n_type = EB_HEADER; + CP(notes.nf2_name, EB_PARAM_NOTE); + notes.nf2_header = virt_to_phys(header); + + notes.nf3.n_namesz = sizeof(EB_PARAM_NOTE); + notes.nf3.n_descsz = sizeof(notes.nf3_meminfo); + notes.nf3.n_type = EB_I386_MEMMAP; + CP(notes.nf3_name, EB_PARAM_NOTE); + memcpy(¬es.nf3_meminfo, &meminfo, sizeof(meminfo)); + + /* Initialize the variable length entries */ + notes.nv1.n_namesz = 0; + notes.nv1.n_descsz = sizeof(NAME); + notes.nv1.n_type = EBN_BOOTLOADER_NAME; + CP(notes.nv1_desc, NAME); + + notes.nv2.n_namesz = 0; + notes.nv2.n_descsz = sizeof(VERSION); + notes.nv2.n_type = EBN_BOOTLOADER_VERSION; + CP(notes.nv2_desc, VERSION); + + notes.nv3.n_namesz = 0; + notes.nv3.n_descsz = sizeof(FIRMWARE); + notes.nv3.n_type = EBN_FIRMWARE_TYPE; + CP(notes.nv3_desc, FIRMWARE); + + /* Attempt to pass the name of the loaded image */ + notes.nv4.n_namesz = 0; + notes.nv4.n_descsz = sizeof(notes.nv4_loaded_image); + notes.nv4.n_type = EBN_LOADED_IMAGE; + memcpy(¬es.nv4_loaded_image, KERNEL_BUF, sizeof(notes.nv4_loaded_image)); + + /* Pass an empty command line for now */ + notes.nv5.n_namesz = 0; + notes.nv5.n_descsz = sizeof(""); + notes.nv5.n_type = EBN_COMMAND_LINE; + CP(notes.nv5_cmdline, ""); + + + notes.hdr.b_checksum = ipchksum(¬es, sizeof(notes)); + /* Like UDP invert a 0 checksum to show that a checksum is present */ + if (notes.hdr.b_checksum == 0) { + notes.hdr.b_checksum = 0xffff; + } + return ¬es.hdr; +} + +int elf_start(unsigned long machine __unused_i386, unsigned long entry, unsigned long params) +{ +#if defined(CONFIG_X86_64) + if (machine == EM_X86_64) { + return xstart_lm(entry, params); + } +#endif + return xstart32(entry, params); +} diff --git a/src/arch/i386/core/etherboot.lds b/src/arch/i386/core/etherboot.lds new file mode 100644 index 00000000..6f406329 --- /dev/null +++ b/src/arch/i386/core/etherboot.lds @@ -0,0 +1,90 @@ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) + +ENTRY(_text) +SECTIONS { + . = ALIGN(16); + /* Start address of Etherboot in the virtual address space */ + _virt_start = 0; + _text = . ; + .text.nocompress : { + *(.text*.nocompress) + . = ALIGN(16); + } = 0x9090 + + .text16 : { + _text16 = .; + *(.text16) + *(.text16.*) + _etext16 = . ; + } + .text.compress : { + *(.text) + *(.text.*) + } = 0x9090 + .rodata : { + . = ALIGN(4); + *(.rodata) + *(.rodata.*) + } + . = ALIGN(4); + .drivers.pci : { + pci_drivers = . ; + *(.drivers.pci); + pci_drivers_end = . ; + } + . = ALIGN(4); + .drivers.isa : { + isa_drivers = . ; + *(.drivers.isa); + isa_drivers_end = .; + } + _etext = . ; + _data = . ; + .data : { + *(.data) + *(.data.*) + } + _edata = . ; + _uncompressed_verbatim_end = . ; + . = ALIGN(16); + .bss.preserve : { + *(.bss.preserve) + *(.bss.preserve.*) + } + _bss = . ; + .bss : { + *(.bss) + *(.bss.*) + } + . = ALIGN(16); + _ebss = .; + _stack = . ; + .stack : { + _stack_start = . ; + *(.stack) + *(.stack.*) + _stack_end = . ; + } + _bss_size = _ebss - _bss; + _stack_offset = _stack - _text ; + _stack_offset_pgh = _stack_offset / 16 ; + _stack_size = _stack_end - _stack_start ; + . = ALIGN(16); + _end = . ; + + /DISCARD/ : { + *(.comment) + *(.note) + } + + /* PXE-specific symbol calculations. The results of these are + * needed in romprefix.S, which is why they must be calculated + * here. + */ + _pxe_stack_size = _pxe_stack_t_size + + _pxe_callback_interface_size + + _rm_callback_interface_size + + _e820mangler_size + 15 ; + +} diff --git a/src/arch/i386/core/etherboot.prefix.lds b/src/arch/i386/core/etherboot.prefix.lds new file mode 100644 index 00000000..3550a2a3 --- /dev/null +++ b/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/src/arch/i386/core/freebsd_loader.c b/src/arch/i386/core/freebsd_loader.c new file mode 100644 index 00000000..4e820e8e --- /dev/null +++ b/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<= 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 = phys_to_virt(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 = phys_to_virt(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 *)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/src/arch/i386/core/hooks.c b/src/arch/i386/core/hooks.c new file mode 100644 index 00000000..9ca6f480 --- /dev/null +++ b/src/arch/i386/core/hooks.c @@ -0,0 +1,35 @@ +#include "etherboot.h" +#include "callbacks.h" +#include + +void arch_main ( in_call_data_t *data __unused, va_list params __unused ) +{ +#ifdef PCBIOS + /* Deallocate base memory used for the prefix, if applicable + */ + forget_prefix_base_memory(); +#endif + +} + +void arch_relocated_from (unsigned long old_addr ) +{ + +#ifdef PCBIOS + /* Deallocate base memory used for the Etherboot runtime, + * if applicable + */ + forget_runtime_base_memory( old_addr ); +#endif + +} + +void arch_on_exit ( int exit_status __unused ) +{ +#ifdef PCBIOS + /* Deallocate the real-mode stack now. We will reallocate + * the stack if are going to use it after this point. + */ + forget_real_mode_stack(); +#endif +} diff --git a/src/arch/i386/core/i386_timer.c b/src/arch/i386/core/i386_timer.c new file mode 100644 index 00000000..531183d4 --- /dev/null +++ b/src/arch/i386/core/i386_timer.c @@ -0,0 +1,191 @@ +/* A couple of routines to implement a low-overhead timer for drivers */ + + /* + * 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 "timer.h" +#include "latch.h" + +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); +} + +#if !defined(CONFIG_TSC_CURRTICKS) +void setup_timers(void) +{ + return; +} + +void load_timer2(unsigned int ticks) +{ + return __load_timer2(ticks); +} + +int timer2_running(void) +{ + return __timer2_running(); +} + +void ndelay(unsigned int nsecs) +{ + waiton_timer2((nsecs * CLOCK_TICK_RATE)/1000000000); +} +void udelay(unsigned int usecs) +{ + waiton_timer2((usecs * TICKS_PER_MS)/1000); +} +#endif /* !defined(CONFIG_TSC_CURRTICKS) */ + +#if defined(CONFIG_TSC_CURRTICKS) + +#define rdtsc(low,high) \ + __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high)) + +#define rdtscll(val) \ + __asm__ __volatile__ ("rdtsc" : "=A" (val)) + + +/* Number of clock ticks to time with the rtc */ +#define LATCH 0xFF + +#define LATCHES_PER_SEC ((CLOCK_TICK_RATE + (LATCH/2))/LATCH) +#define TICKS_PER_LATCH ((LATCHES_PER_SEC + (TICKS_PER_SEC/2))/TICKS_PER_SEC) + +static void sleep_latch(void) +{ + __load_timer2(LATCH); + while(__timer2_running()); +} + +/* ------ Calibrate the TSC ------- + * Time how long it takes to excute a loop that runs in known time. + * And find the convertion needed to get to CLOCK_TICK_RATE + */ + + +static unsigned long long calibrate_tsc(void) +{ + unsigned long startlow, starthigh; + unsigned long endlow, endhigh; + + rdtsc(startlow,starthigh); + sleep_latch(); + rdtsc(endlow,endhigh); + + /* 64-bit subtract - gcc just messes up with long longs */ + __asm__("subl %2,%0\n\t" + "sbbl %3,%1" + :"=a" (endlow), "=d" (endhigh) + :"g" (startlow), "g" (starthigh), + "0" (endlow), "1" (endhigh)); + + /* Error: ECPUTOOFAST */ + if (endhigh) + goto bad_ctc; + + endlow *= TICKS_PER_LATCH; + return endlow; + + /* + * The CTC wasn't reliable: we got a hit on the very first read, + * or the CPU was so fast/slow that the quotient wouldn't fit in + * 32 bits.. + */ +bad_ctc: + printf("bad_ctc\n"); + return 0; +} + +static unsigned long clocks_per_tick; +void setup_timers(void) +{ + if (!clocks_per_tick) { + clocks_per_tick = calibrate_tsc(); + /* Display the CPU Mhz to easily test if the calibration was bad */ + printf("CPU %ld Mhz\n", (clocks_per_tick/1000 * TICKS_PER_SEC)/1000); + } +} + +unsigned long currticks(void) +{ + unsigned long clocks_high, clocks_low; + unsigned long currticks; + /* Read the Time Stamp Counter */ + rdtsc(clocks_low, clocks_high); + + /* currticks = clocks / clocks_per_tick; */ + __asm__("divl %1" + :"=a" (currticks) + :"r" (clocks_per_tick), "0" (clocks_low), "d" (clocks_high)); + + + return currticks; +} + +static unsigned long long timer_timeout; +static int __timer_running(void) +{ + unsigned long long now; + rdtscll(now); + return now < timer_timeout; +} + +void udelay(unsigned int usecs) +{ + unsigned long long now; + rdtscll(now); + timer_timeout = now + usecs * ((clocks_per_tick * TICKS_PER_SEC)/(1000*1000)); + while(__timer_running()); +} +void ndelay(unsigned int nsecs) +{ + unsigned long long now; + rdtscll(now); + timer_timeout = now + nsecs * ((clocks_per_tick * TICKS_PER_SEC)/(1000*1000*1000)); + while(__timer_running()); +} + +void load_timer2(unsigned int timer2_ticks) +{ + unsigned long long now; + unsigned long clocks; + rdtscll(now); + clocks = timer2_ticks * ((clocks_per_tick * TICKS_PER_SEC)/CLOCK_TICK_RATE); + timer_timeout = now + clocks; +} + +int timer2_running(void) +{ + return __timer_running(); +} + +#endif /* RTC_CURRTICKS */ diff --git a/src/arch/i386/core/init.S b/src/arch/i386/core/init.S new file mode 100644 index 00000000..71717c25 --- /dev/null +++ b/src/arch/i386/core/init.S @@ -0,0 +1,305 @@ +#include "callbacks.h" + .equ CR0_PE, 1 + + .text + .arch i386 + .section ".prefix", "ax", @progbits + +#undef CODE16 +#if defined(PCBIOS) +#define CODE16 +#endif + +/* We have two entry points: "conventional" (at the start of the file) + * and "callback" (at _entry, 2 bytes in). The "callback" entry + * should be used if the caller wishes to provide a specific opcode. + * It is equivalent to a call to in_call. Using the "conventional" + * entry point is equivalent to using the "callback" entry point with + * an opcode of EB_OPCODE_MAIN. + * + * Both entry points can be called in either 16-bit real or 32-bit + * protected mode with flat physical addresses. We detect which mode + * the processor is in and call either in_call or rm_in_call as + * appropriate. Note that the mode detection code must therefore be + * capable of doing the same thing in either mode, even though the + * machine code instructions will be interpreted differently. + * + * The decompressor will be invoked if necessary to decompress + * Etherboot before attempting to jump to it. + */ + +/****************************************************************************** + * Entry points and mode detection code + ****************************************************************************** + */ + + .code32 +/* "Conventional" entry point: caller provides no opcode */ + .globl _start +_start: + /* Set flag to indicate conventional entry point used */ + pushl $0 /* "pushw $0" in 16-bit code */ + /* Fall through to "callback" entry point */ + +/* "Callback" entry point */ + .globl _entry +_entry: + +#ifdef CODE16 + /* CPU mode detection code */ + pushl %eax /* "pushw %ax" in 16-bit code */ + pushw %ax /* "pushl %eax" in 16-bit code */ + movl %cr0, %eax /* Test protected mode bit */ + testb $CR0_PE, %al + popw %ax /* "popl %eax" in 16-bit code */ + popl %eax /* "popw %eax" in 16-bit code */ + jz rmode +#endif /* CODE16 */ + +/****************************************************************************** + * Entered in protected mode + ****************************************************************************** + */ + + .code32 +pmode: + cmpl $0, 0(%esp) /* Conventional entry point used? */ + jne 1f + /* Entered via conventional entry point: set up stack */ + xchgl %eax, 4(%esp) /* %eax = return addr, store %eax */ + movl %eax, 0(%esp) /* 0(%esp) = return address */ + movl $(EB_OPCODE_MAIN|EB_USE_INTERNAL_STACK|EB_SKIP_OPCODE), %eax + xchgl %eax, 4(%esp) /* 4(%esp) = opcode, restore %eax */ +1: + /* Run decompressor if necessary */ + pushl %eax + movl $decompress, %eax + testl %eax, %eax + jz 1f + call decompress +1: popl %eax + + /* Make in_call to Etherboot */ + jmp _prefix_in_call + +/****************************************************************************** + * Entered in real mode + ****************************************************************************** + */ + +#ifdef CODE16 + .code16 +rmode: + pushw %ax /* Padding */ + pushw %bp + movw %sp, %bp + cmpw $0, 6(%bp) /* Conventional entry point used? */ + jne 1f + /* Entered via conventional entry point: set up stack */ + pushw %ax + movw 6(%bp), %ax + movw %ax, 2(%bp) /* Move return address down */ + movl $(EB_OPCODE_MAIN|EB_USE_INTERNAL_STACK|EB_SKIP_OPCODE), 4(%bp) + popw %ax + popw %bp + jmp 2f +1: /* Entered via callback entry point: do nothing */ + popw %bp + popw %ax +2: + /* Preserve registers */ + pushw %ds + pushl %eax + + /* Run decompressor if necessary. Decompressor is 32-bit + * code, so we must switch to pmode first. Save and restore + * GDT over transition to pmode. + */ + movl $decompress, %eax + testl %eax, %eax + jz 1f + pushw %ds + pushw %es + pushw %fs + pushw %gs + subw $8, %sp + pushw %bp + movw %sp, %bp + sgdt 2(%bp) + pushw %ss /* Store params for _prot_to_real */ + pushw %cs + call _prefix_real_to_prot + .code32 + call decompress + call _prefix_prot_to_real + .code16 + popw %ax /* skip */ + popw %ax /* skip */ + lgdt 2(%bp) + popw %bp + addw $8, %sp + popw %gs + popw %fs + popw %es + popw %ds +1: + + /* Set rm_etherboot_location */ + xorl %eax, %eax + movw %cs, %ax + movw %ax, %ds + shll $4, %eax + addl $_prefix_size, %eax + movl %eax, _prefix_rm_etherboot_location + + /* Restore registers */ + popl %eax + popw %ds + + /* Make real-mode in_call to Etherboot */ + jmp _prefix_rm_in_call +#endif /* CODE16 */ + +/****************************************************************************** + * Utility routines that can be called by the "prefix". + ****************************************************************************** + */ + +#ifdef CODE16 + +/* Prelocate code: either to an area at the top of free base memory. + * Switch stacks to use the stack within the resulting + * Etherboot image. + * + * On entry, %cs:0000 must be the start of the prefix: this is used to + * locate the code to be copied. + * + * This routine takes a single word parameter: the number of bytes to + * be transferred from the old stack to the new stack (excluding the + * return address and this parameter itself, which will always be + * copied). If this value is negative, the stacks will not be + * switched. + * + * Control will "return" to the appropriate point in the relocated + * image. + */ + +#define PRELOC_PRESERVE ( 20 ) +#define PRELOC_OFFSET_RETADDR ( PRELOC_PRESERVE ) +#define PRELOC_OFFSET_RETADDR_E ( PRELOC_OFFSET_RETADDR + 4 ) +#define PRELOC_OFFSET_COPY ( PRELOC_OFFSET_RETADDR_E ) +#define PRELOC_OFFSET_COPY_E ( PRELOC_OFFSET_COPY + 2 ) + +#define PRELOC_ALWAYS_COPY ( PRELOC_OFFSET_COPY_E ) + + .code16 + .globl prelocate +prelocate: + /* Pad to allow for expansion of return address */ + pushw %ax + + /* Preserve registers */ + pushaw + pushw %ds + pushw %es + + /* Claim an area of base memory from the BIOS and put the + * payload there. + */ + movw $0x40, %bx + movw %bx, %es + movw %es:(0x13), %bx /* FBMS in kb to %ax */ + shlw $6, %bx /* ... in paragraphs */ + subw $_image_size_pgh, %bx /* Subtract space for image */ + shrw $6, %bx /* Round down to nearest kb */ + movw %bx, %es:(0x13) /* ...and claim memory from BIOS */ + shlw $6, %bx + + /* At this point %bx contains the segment address for the + * start of the image (image = prefix + runtime). + */ + + /* Switch stacks */ + movw %ss, %ax + movw %ax, %ds + movw %sp, %si /* %ds:si = current %ss:sp */ + movw %ss:PRELOC_OFFSET_COPY(%si), %cx + testw %cx, %cx + js 1f + leaw _stack_offset_pgh(%bx), %ax /* %ax = new %ss */ + movw %ax, %es + movw $_stack_size, %di + addw $PRELOC_ALWAYS_COPY, %cx + subw %cx, %di /* %es:di = new %ss:sp */ + movw %ax, %ss /* Set new %ss:sp */ + movw %di, %sp + cld + rep movsb /* Copy stack contents */ +1: + + /* Do the image copy backwards, since if there's overlap with + * a forward copy then it means we're going to get trashed + * during the copy anyway... + */ + pushal /* Preserve 32-bit registers */ + movw %bx, %es /* Destination base for copy */ + pushw %cs + popw %ds /* Source base for copy */ + movl $_verbatim_size-1, %ecx /* Offset to last byte */ + movl %ecx, %esi + movl %ecx, %edi + incl %ecx /* Length */ + std /* Backwards copy of binary */ + ADDR32 rep movsb + cld + popal /* Restore 32-bit registers */ + + /* Store (%bx<<4) as image_basemem to be picked up by + * basemem.c. Also store image_size, since there's no other + * way that we can later know how much memory we allocated. + * (_zfile_size is unavailable when rt2 is linked). + */ + pushl %eax + xorl %eax, %eax + movw %bx, %ax + shll $4, %eax + movl %eax, %es:_prefix_image_basemem + movl $_image_size, %es:_prefix_image_basemem_size + popl %eax + + /* Expand original near return address into far return to new + * code location. + */ + movw %sp, %bp + xchgw %bx, (PRELOC_OFFSET_RETADDR+2)(%bp) + movw %bx, (PRELOC_OFFSET_RETADDR+0)(%bp) + + /* Restore registers and return */ + popw %es + popw %ds + popaw + lret /* Jump to relocated code */ + + /* Utility routine to free base memory allocated by prelocate. + * Ensure that said memory is not in use (e.g. for the CPU + * stack) before calling this routine. + */ + .globl deprelocate +deprelocate: + /* Claim an area of base memory from the BIOS and put the + * payload there. + */ + pushw %ax + pushw %es + movw $0x40, %ax + movw %ax, %es + movw %es:(0x13), %ax /* FBMS in kb to %ax */ + shlw $6, %ax /* ... in paragraphs */ + addw $_image_size_pgh+0x40-1, %ax /* Add space for image and... */ + shrw $6, %ax /* ...round up to nearest kb */ + movw %ax, %es:(0x13) /* Give memory back to BIOS */ + popw %es + popw %ax + ret + +#endif /* CODE16 */ diff --git a/src/arch/i386/core/multiboot_loader.c b/src/arch/i386/core/multiboot_loader.c new file mode 100644 index 00000000..e9785f1c --- /dev/null +++ b/src/arch/i386/core/multiboot_loader.c @@ -0,0 +1,143 @@ +/* Multiboot support + * + * 2003-07-02 mmap fix and header probe by SONE Takeshi + */ + +struct multiboot_mods { + unsigned mod_start; + unsigned mod_end; + unsigned char *string; + unsigned reserved; +}; + +struct multiboot_mmap { + unsigned int size; + unsigned int base_addr_low; + unsigned int base_addr_high; + unsigned int length_low; + unsigned int length_high; + unsigned int type; +}; + +/* The structure of a Multiboot 0.6 parameter block. */ +struct multiboot_info { + unsigned int flags; +#define MULTIBOOT_MEM_VALID 0x01 +#define MULTIBOOT_BOOT_DEV_VALID 0x02 +#define MULTIBOOT_CMDLINE_VALID 0x04 +#define MULTIBOOT_MODS_VALID 0x08 +#define MULTIBOOT_AOUT_SYMS_VALID 0x10 +#define MULTIBOOT_ELF_SYMS_VALID 0x20 +#define MULTIBOOT_MMAP_VALID 0x40 + unsigned int memlower; + unsigned int memupper; + unsigned int bootdev; + unsigned int cmdline; /* physical address of the command line */ + unsigned mods_count; + struct multiboot_mods *mods_addr; + unsigned syms_num; + unsigned syms_size; + unsigned syms_addr; + unsigned syms_shndx; + unsigned mmap_length; + unsigned mmap_addr; + /* The structure actually ends here, so I might as well put + * the ugly e820 parameters here... + */ + struct multiboot_mmap mmap[E820MAX]; +}; + +/* Multiboot image header (minimal part) */ +struct multiboot_header { + unsigned int magic; +#define MULTIBOOT_HEADER_MAGIC 0x1BADB002 + unsigned int flags; + unsigned int checksum; +}; + +static struct multiboot_header *mbheader; + +static struct multiboot_info mbinfo; + +static void multiboot_probe(unsigned char *data, int len) +{ + int offset; + struct multiboot_header *h; + + /* Multiboot spec requires the header to be in first 8KB of the image */ + if (len > 8192) + len = 8192; + + for (offset = 0; offset < len; offset += 4) { + h = (struct multiboot_header *) (data + offset); + if (h->magic == MULTIBOOT_HEADER_MAGIC + && h->magic + h->flags + h->checksum == 0) { + printf("/Multiboot"); + mbheader = h; + return; + } + } + mbheader = 0; +} + +static inline void multiboot_boot(unsigned long entry) +{ + unsigned char cmdline[512], *c; + int i; + if (!mbheader) + return; + /* Etherboot limits the command line to the kernel name, + * default parameters and user prompted parameters. All of + * them are shorter than 256 bytes. As the kernel name and + * the default parameters come from the same BOOTP/DHCP entry + * (or if they don't, the parameters are empty), only two + * strings of the maximum size are possible. Note this buffer + * can overrun if a stupid file name is chosen. Oh well. */ + c = cmdline; + for (i = 0; KERNEL_BUF[i] != 0; i++) { + switch (KERNEL_BUF[i]) { + case ' ': + case '\\': + case '"': + *c++ = '\\'; + break; + default: + break; + } + *c++ = KERNEL_BUF[i]; + } + (void)sprintf(c, " -retaddr %#lX", virt_to_phys(xend32)); + + mbinfo.flags = MULTIBOOT_MMAP_VALID | MULTIBOOT_MEM_VALID |MULTIBOOT_CMDLINE_VALID; + mbinfo.memlower = meminfo.basememsize; + mbinfo.memupper = meminfo.memsize; + mbinfo.bootdev = 0; /* not booted from disk */ + mbinfo.cmdline = virt_to_phys(cmdline); + for (i = 0; i < (int) meminfo.map_count; i++) { + mbinfo.mmap[i].size = sizeof(struct multiboot_mmap) + - sizeof(unsigned int); + mbinfo.mmap[i].base_addr_low = + (unsigned int) meminfo.map[i].addr; + mbinfo.mmap[i].base_addr_high = + (unsigned int) (meminfo.map[i].addr >> 32); + mbinfo.mmap[i].length_low = + (unsigned int) meminfo.map[i].size; + mbinfo.mmap[i].length_high = + (unsigned int) (meminfo.map[i].size >> 32); + mbinfo.mmap[i].type = meminfo.map[i].type; + } + mbinfo.mmap_length = meminfo.map_count * sizeof(struct multiboot_mmap); + mbinfo.mmap_addr = virt_to_phys(mbinfo.mmap); + + /* The Multiboot 0.6 spec requires all segment registers to be + * loaded with an unrestricted, writeable segment. + * xstart32 does this for us. + */ + + /* Start the kernel, passing the Multiboot information record + * and the magic number. */ + os_regs.eax = 0x2BADB002; + os_regs.ebx = virt_to_phys(&mbinfo); + xstart32(entry); + longjmp(restart_etherboot, -2); +} diff --git a/src/arch/i386/core/pci_io.c b/src/arch/i386/core/pci_io.c new file mode 100644 index 00000000..e89561e4 --- /dev/null +++ b/src/arch/i386/core/pci_io.c @@ -0,0 +1,352 @@ +/* +** Support for NE2000 PCI clones added David Monro June 1997 +** Generalised to other NICs by Ken Yap July 1997 +** +** Most of this is taken from: +** +** /usr/src/linux/drivers/pci/pci.c +** /usr/src/linux/include/linux/pci.h +** /usr/src/linux/arch/i386/bios32.c +** /usr/src/linux/include/linux/bios32.h +** /usr/src/linux/drivers/net/ne.c +*/ +#ifdef CONFIG_PCI +#include "etherboot.h" +#include "pci.h" + +#ifdef CONFIG_PCI_DIRECT +#define PCIBIOS_SUCCESSFUL 0x00 + +#define DEBUG 0 + +/* + * Functions for accessing PCI configuration space with type 1 accesses + */ + +#define CONFIG_CMD(bus, device_fn, where) (0x80000000 | (bus << 16) | (device_fn << 8) | (where & ~3)) + +int pcibios_read_config_byte(unsigned int bus, unsigned int device_fn, + unsigned int where, uint8_t *value) +{ + outl(CONFIG_CMD(bus,device_fn,where), 0xCF8); + *value = inb(0xCFC + (where&3)); + return PCIBIOS_SUCCESSFUL; +} + +int pcibios_read_config_word (unsigned int bus, + unsigned int device_fn, unsigned int where, uint16_t *value) +{ + outl(CONFIG_CMD(bus,device_fn,where), 0xCF8); + *value = inw(0xCFC + (where&2)); + return PCIBIOS_SUCCESSFUL; +} + +int pcibios_read_config_dword (unsigned int bus, unsigned int device_fn, + unsigned int where, uint32_t *value) +{ + outl(CONFIG_CMD(bus,device_fn,where), 0xCF8); + *value = inl(0xCFC); + return PCIBIOS_SUCCESSFUL; +} + +int pcibios_write_config_byte (unsigned int bus, unsigned int device_fn, + unsigned int where, uint8_t value) +{ + outl(CONFIG_CMD(bus,device_fn,where), 0xCF8); + outb(value, 0xCFC + (where&3)); + return PCIBIOS_SUCCESSFUL; +} + +int pcibios_write_config_word (unsigned int bus, unsigned int device_fn, + unsigned int where, uint16_t value) +{ + outl(CONFIG_CMD(bus,device_fn,where), 0xCF8); + outw(value, 0xCFC + (where&2)); + return PCIBIOS_SUCCESSFUL; +} + +int pcibios_write_config_dword (unsigned int bus, unsigned int device_fn, unsigned int where, uint32_t value) +{ + outl(CONFIG_CMD(bus,device_fn,where), 0xCF8); + outl(value, 0xCFC); + return PCIBIOS_SUCCESSFUL; +} + +#undef CONFIG_CMD + +#else /* CONFIG_PCI_DIRECT not defined */ + +#if !defined(PCBIOS) +#error "The pcibios can only be used when the PCBIOS support is compiled in" +#endif + +/* Macro for calling the BIOS32 service. This replaces the old + * bios32_call function. Use in a statement such as + * __asm__ ( BIOS32_CALL, + * : + * : "S" ( bios32_entry ), ); + */ +#define BIOS32_CALL "call _virt_to_phys\n\t" \ + "pushl %%cs\n\t" \ + "call *%%esi\n\t" \ + "cli\n\t" \ + "cld\n\t" \ + "call _phys_to_virt\n\t" + +static unsigned long bios32_entry; +static unsigned long pcibios_entry; + +static unsigned long bios32_service(unsigned long service) +{ + unsigned char return_code; /* %al */ + unsigned long address; /* %ebx */ + unsigned long length; /* %ecx */ + unsigned long entry; /* %edx */ + + __asm__(BIOS32_CALL + : "=a" (return_code), + "=b" (address), + "=c" (length), + "=d" (entry) + : "0" (service), + "1" (0), + "S" (bios32_entry)); + + switch (return_code) { + case 0: + return address + entry; + case 0x80: /* Not present */ + printf("bios32_service(%d) : not present\n", service); + return 0; + default: /* Shouldn't happen */ + printf("bios32_service(%d) : returned %#X????\n", + service, return_code); + return 0; + } +} + +int pcibios_read_config_byte(unsigned int bus, + unsigned int device_fn, unsigned int where, uint8_t *value) +{ + unsigned long ret; + unsigned long bx = (bus << 8) | device_fn; + + __asm__(BIOS32_CALL + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=c" (*value), + "=a" (ret) + : "1" (PCIBIOS_READ_CONFIG_BYTE), + "b" (bx), + "D" ((long) where), + "S" (pcibios_entry)); + return (int) (ret & 0xff00) >> 8; +} + +int pcibios_read_config_word(unsigned int bus, + unsigned int device_fn, unsigned int where, uint16_t *value) +{ + unsigned long ret; + unsigned long bx = (bus << 8) | device_fn; + + __asm__(BIOS32_CALL + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=c" (*value), + "=a" (ret) + : "1" (PCIBIOS_READ_CONFIG_WORD), + "b" (bx), + "D" ((long) where), + "S" (pcibios_entry)); + return (int) (ret & 0xff00) >> 8; +} + +int pcibios_read_config_dword(unsigned int bus, + unsigned int device_fn, unsigned int where, uint32_t *value) +{ + unsigned long ret; + unsigned long bx = (bus << 8) | device_fn; + + __asm__(BIOS32_CALL + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=c" (*value), + "=a" (ret) + : "1" (PCIBIOS_READ_CONFIG_DWORD), + "b" (bx), + "D" ((long) where), + "S" (pcibios_entry)); + return (int) (ret & 0xff00) >> 8; +} + +int pcibios_write_config_byte (unsigned int bus, + unsigned int device_fn, unsigned int where, uint8_t value) +{ + unsigned long ret; + unsigned long bx = (bus << 8) | device_fn; + + __asm__(BIOS32_CALL + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=a" (ret) + : "0" (PCIBIOS_WRITE_CONFIG_BYTE), + "c" (value), + "b" (bx), + "D" ((long) where), + "S" (pcibios_entry)); + return (int) (ret & 0xff00) >> 8; +} + +int pcibios_write_config_word (unsigned int bus, + unsigned int device_fn, unsigned int where, uint16_t value) +{ + unsigned long ret; + unsigned long bx = (bus << 8) | device_fn; + + __asm__(BIOS32_CALL + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=a" (ret) + : "0" (PCIBIOS_WRITE_CONFIG_WORD), + "c" (value), + "b" (bx), + "D" ((long) where), + "S" (pcibios_entry)); + return (int) (ret & 0xff00) >> 8; +} + +int pcibios_write_config_dword (unsigned int bus, + unsigned int device_fn, unsigned int where, uint32_t value) +{ + unsigned long ret; + unsigned long bx = (bus << 8) | device_fn; + + __asm__(BIOS32_CALL + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=a" (ret) + : "0" (PCIBIOS_WRITE_CONFIG_DWORD), + "c" (value), + "b" (bx), + "D" ((long) where), + "S" (pcibios_entry)); + return (int) (ret & 0xff00) >> 8; +} + +static void check_pcibios(void) +{ + unsigned long signature; + unsigned char present_status; + unsigned char major_revision; + unsigned char minor_revision; + int pack; + + if ((pcibios_entry = bios32_service(PCI_SERVICE))) { + __asm__(BIOS32_CALL + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:\tshl $8, %%eax\n\t" + "movw %%bx, %%ax" + : "=d" (signature), + "=a" (pack) + : "1" (PCIBIOS_PCI_BIOS_PRESENT), + "S" (pcibios_entry) + : "bx", "cx"); + + present_status = (pack >> 16) & 0xff; + major_revision = (pack >> 8) & 0xff; + minor_revision = pack & 0xff; + if (present_status || (signature != PCI_SIGNATURE)) { + printf("ERROR: BIOS32 says PCI BIOS, but no PCI " + "BIOS????\n"); + pcibios_entry = 0; + } +#if DEBUG + if (pcibios_entry) { + printf ("pcibios_init : PCI BIOS revision %hhX.%hhX" + " entry at %#X\n", major_revision, + minor_revision, pcibios_entry); + } +#endif + } +} + +static void pcibios_init(void) +{ + union bios32 *check; + unsigned char sum; + int i, length; + bios32_entry = 0; + + /* + * Follow the standard procedure for locating the BIOS32 Service + * directory by scanning the permissible address range from + * 0xe0000 through 0xfffff for a valid BIOS32 structure. + * + */ + + for (check = phys_to_virt(0xe0000); (void *)check <= phys_to_virt(0xffff0); ++check) { + if (check->fields.signature != BIOS32_SIGNATURE) + continue; + length = check->fields.length * 16; + if (!length) + continue; + sum = 0; + for (i = 0; i < length ; ++i) + sum += check->chars[i]; + if (sum != 0) + continue; + if (check->fields.revision != 0) { + printf("pcibios_init : unsupported revision %d at %#X, mail drew@colorado.edu\n", + check->fields.revision, check); + continue; + } +#if DEBUG + printf("pcibios_init : BIOS32 Service Directory " + "structure at %#X\n", check); +#endif + if (!bios32_entry) { + if (check->fields.entry >= 0x100000) { + printf("pcibios_init: entry in high " + "memory, giving up\n"); + return; + } else { + bios32_entry = check->fields.entry; +#if DEBUG + printf("pcibios_init : BIOS32 Service Directory" + " entry at %#X\n", bios32_entry); +#endif + } + } + } + if (bios32_entry) + check_pcibios(); +} +#endif /* CONFIG_PCI_DIRECT not defined*/ + +unsigned long pcibios_bus_base(unsigned int bus __unused) +{ + /* architecturally this must be 0 */ + return 0; +} + +void find_pci(int type, struct pci_device *dev) +{ +#ifndef CONFIG_PCI_DIRECT + if (!pcibios_entry) { + pcibios_init(); + } + if (!pcibios_entry) { + printf("pci_init: no BIOS32 detected\n"); + return; + } +#endif + return scan_pci_bus(type, dev); +} +#endif /* CONFIG_PCI */ diff --git a/src/arch/i386/core/pic8259.c b/src/arch/i386/core/pic8259.c new file mode 100644 index 00000000..cc2071fc --- /dev/null +++ b/src/arch/i386/core/pic8259.c @@ -0,0 +1,331 @@ +/* + * Basic support for controlling the 8259 Programmable Interrupt Controllers. + * + * Initially written by Michael Brown (mcb30). + */ + +#include +#include "pic8259.h" +#include "realmode.h" + +#ifdef DEBUG_IRQ +#define DBG(...) printf ( __VA_ARGS__ ) +#else +#define DBG(...) +#endif + +/* State of trivial IRQ handler */ +irq_t trivial_irq_installed_on = IRQ_NONE; +static uint16_t trivial_irq_previous_trigger_count = 0; + +/* The actual trivial IRQ handler + * + * Note: we depend on the C compiler not realising that we're putting + * variables in the ".text16" section and therefore not forcing them + * back to the ".data" section. I don't see any reason to expect this + * behaviour to change. + * + * These must *not* be the first variables to appear in this file; the + * first variable to appear gets the ".data" directive. + */ +RM_FRAGMENT(_trivial_irq_handler, + "pushw %bx\n\t" + "call 1f\n1:\tpopw %bx\n\t" /* PIC access to variables */ + "incw %cs:(_trivial_irq_trigger_count-1b)(%bx)\n\t" + "popw %bx\n\t" + "iret\n\t" + "\n\t" + ".globl _trivial_irq_trigger_count\n\t" + "_trivial_irq_trigger_count: .short 0\n\t" + "\n\t" + ".globl _trivial_irq_chain_to\n\t" + "_trivial_irq_chain_to: .short 0,0\n\t" + "\n\t" + ".globl _trivial_irq_chain\n\t" + "_trivial_irq_chain: .byte 0\n\t" + ); +extern volatile uint16_t _trivial_irq_trigger_count; +extern segoff_t _trivial_irq_chain_to; +extern int8_t _trivial_irq_chain; + +/* Current locations of trivial IRQ handler. These will change at + * runtime when relocation is used; the handler needs to be copied to + * base memory before being installed. + */ +void (*trivial_irq_handler)P((void)) = _trivial_irq_handler; +uint16_t volatile *trivial_irq_trigger_count = &_trivial_irq_trigger_count; +segoff_t *trivial_irq_chain_to = &_trivial_irq_chain_to; +uint8_t *trivial_irq_chain = &_trivial_irq_chain; + +/* Install a handler for the specified IRQ. Address of previous + * handler will be stored in previous_handler. Enabled/disabled state + * of IRQ will be preserved across call, therefore if the handler does + * chaining, ensure that either (a) IRQ is disabled before call, or + * (b) previous_handler points directly to the place that the handler + * picks up its chain-to address. + */ + +int install_irq_handler ( irq_t irq, segoff_t *handler, + uint8_t *previously_enabled, + segoff_t *previous_handler ) { + segoff_t *irq_vector = IRQ_VECTOR ( irq ); + *previously_enabled = irq_enabled ( irq ); + + if ( irq > IRQ_MAX ) { + DBG ( "Invalid IRQ number %d\n" ); + return 0; + } + + previous_handler->segment = irq_vector->segment; + previous_handler->offset = irq_vector->offset; + if ( *previously_enabled ) disable_irq ( irq ); + DBG ( "Installing handler at %hx:%hx for IRQ %d (vector 0000:%hx)," + " leaving %s\n", + handler->segment, handler->offset, irq, virt_to_phys(irq_vector), + ( *previously_enabled ? "enabled" : "disabled" ) ); + DBG ( "...(previous handler at %hx:%hx)\n", + previous_handler->segment, previous_handler->offset ); + irq_vector->segment = handler->segment; + irq_vector->offset = handler->offset; + if ( *previously_enabled ) enable_irq ( irq ); + return 1; +} + +/* Remove handler for the specified IRQ. Routine checks that another + * handler has not been installed that chains to handler before + * uninstalling handler. Enabled/disabled state of the IRQ will be + * restored to that specified by previously_enabled. + */ + +int remove_irq_handler ( irq_t irq, segoff_t *handler, + uint8_t *previously_enabled, + segoff_t *previous_handler ) { + segoff_t *irq_vector = IRQ_VECTOR ( irq ); + + if ( irq > IRQ_MAX ) { + DBG ( "Invalid IRQ number %d\n" ); + return 0; + } + if ( ( irq_vector->segment != handler->segment ) || + ( irq_vector->offset != handler->offset ) ) { + DBG ( "Cannot remove handler for IRQ %d\n" ); + return 0; + } + + DBG ( "Removing handler for IRQ %d\n", irq ); + disable_irq ( irq ); + irq_vector->segment = previous_handler->segment; + irq_vector->offset = previous_handler->offset; + if ( *previously_enabled ) enable_irq ( irq ); + return 1; +} + +/* Install the trivial IRQ handler. This routine installs the + * handler, tests it and enables the IRQ. + */ + +int install_trivial_irq_handler ( irq_t irq ) { + segoff_t trivial_irq_handler_segoff = SEGOFF(trivial_irq_handler); + + if ( trivial_irq_installed_on != IRQ_NONE ) { + DBG ( "Can install trivial IRQ handler only once\n" ); + return 0; + } + if ( SEGMENT(trivial_irq_handler) > 0xffff ) { + DBG ( "Trivial IRQ handler not in base memory\n" ); + return 0; + } + + DBG ( "Installing trivial IRQ handler on IRQ %d\n", irq ); + if ( ! install_irq_handler ( irq, &trivial_irq_handler_segoff, + trivial_irq_chain, + trivial_irq_chain_to ) ) + return 0; + trivial_irq_installed_on = irq; + + DBG ( "Testing trivial IRQ handler\n" ); + disable_irq ( irq ); + *trivial_irq_trigger_count = 0; + trivial_irq_previous_trigger_count = 0; + fake_irq ( irq ); + if ( ! trivial_irq_triggered ( irq ) ) { + DBG ( "Installation of trivial IRQ handler failed\n" ); + remove_trivial_irq_handler ( irq ); + return 0; + } + /* Send EOI just in case there was a leftover interrupt */ + send_specific_eoi ( irq ); + DBG ( "Trivial IRQ handler installed successfully\n" ); + enable_irq ( irq ); + return 1; +} + +/* Remove the trivial IRQ handler. + */ + +int remove_trivial_irq_handler ( irq_t irq ) { + segoff_t trivial_irq_handler_segoff = SEGOFF(trivial_irq_handler); + + if ( trivial_irq_installed_on == IRQ_NONE ) return 1; + if ( irq != trivial_irq_installed_on ) { + DBG ( "Cannot uninstall trivial IRQ handler from IRQ %d; " + "is installed on IRQ %d\n", irq, + trivial_irq_installed_on ); + return 0; + } + + if ( ! remove_irq_handler ( irq, &trivial_irq_handler_segoff, + trivial_irq_chain, + trivial_irq_chain_to ) ) + return 0; + + if ( trivial_irq_triggered ( trivial_irq_installed_on ) ) { + DBG ( "Sending EOI for unwanted trivial IRQ\n" ); + send_specific_eoi ( trivial_irq_installed_on ); + } + + trivial_irq_installed_on = IRQ_NONE; + return 1; +} + +/* Safe method to detect whether or not trivial IRQ has been + * triggered. Using this call avoids potential race conditions. This + * call will return success only once per trigger. + */ + +int trivial_irq_triggered ( irq_t irq ) { + uint16_t trivial_irq_this_trigger_count = *trivial_irq_trigger_count; + int triggered = ( trivial_irq_this_trigger_count - + trivial_irq_previous_trigger_count ); + + /* irq is not used at present, but we have it in the API for + * future-proofing; in case we want the facility to have + * multiple trivial IRQ handlers installed simultaneously. + * + * Avoid compiler warning about unused variable. + */ + if ( irq == IRQ_NONE ) {}; + + trivial_irq_previous_trigger_count = trivial_irq_this_trigger_count; + return triggered ? 1 : 0; +} + +/* Copy trivial IRQ handler to a new location. Typically used to copy + * the handler into base memory; when relocation is being used we need + * to do this before installing the handler. + * + * Call with target=NULL in order to restore the handler to its + * original location. + */ + +int copy_trivial_irq_handler ( void *target, size_t target_size ) { + irq_t currently_installed_on = trivial_irq_installed_on; + uint32_t offset = ( target == NULL ? 0 : + target - (void*)_trivial_irq_handler ); + + if (( target != NULL ) && ( target_size < TRIVIAL_IRQ_HANDLER_SIZE )) { + DBG ( "Insufficient space to copy trivial IRQ handler\n" ); + return 0; + } + + if ( currently_installed_on != IRQ_NONE ) { + DBG ("WARNING: relocating trivial IRQ handler while in use\n"); + if ( ! remove_trivial_irq_handler ( currently_installed_on ) ) + return 0; + } + + /* Do the actual copy */ + if ( target != NULL ) { + DBG ( "Copying trivial IRQ handler to %hx:%hx\n", + SEGMENT(target), OFFSET(target) ); + memcpy ( target, _trivial_irq_handler, + TRIVIAL_IRQ_HANDLER_SIZE ); + } else { + DBG ( "Restoring trivial IRQ handler to original location\n" ); + } + /* Update all the pointers to structures within the handler */ + trivial_irq_handler = ( void (*)P((void)) ) + ( (void*)_trivial_irq_handler + offset ); + trivial_irq_trigger_count = (uint16_t*) + ( (void*)&_trivial_irq_trigger_count + offset ); + trivial_irq_chain_to = (segoff_t*) + ( (void*)&_trivial_irq_chain_to + offset ); + trivial_irq_chain = (uint8_t*) + ( (void*)&_trivial_irq_chain + offset ); + + if ( currently_installed_on != IRQ_NONE ) { + if ( ! install_trivial_irq_handler ( currently_installed_on ) ) + return 0; + } + return 1; +} + +/* Send non-specific EOI(s). This seems to be inherently unsafe. + */ + +void send_nonspecific_eoi ( irq_t 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). + */ + +void send_specific_eoi ( irq_t irq ) { + DBG ( "Sending specific EOI for IRQ %d\n", irq ); + outb ( ICR_EOI_SPECIFIC | ICR_VALUE(irq), ICR_REG(irq) ); + if ( irq >= IRQ_PIC_CUTOFF ) { + outb ( ICR_EOI_SPECIFIC | ICR_VALUE(CHAINED_IRQ), + ICR_REG(CHAINED_IRQ) ); + } +} + +/* Fake an IRQ + */ + +void fake_irq ( irq_t irq ) { + struct { + uint16_t int_number; + } PACKED in_stack; + + /* Convert IRQ to INT number: + * + * subb $0x08,%cl Invert bit 3, set bits 4-7 iff irq < 8 + * xorb $0x70,%cl Invert bits 4-6 + * andb $0x7f,%cl Clear bit 7 + * + * No, it's not the most intuitive method, but I was proud to + * get it down to three lines of assembler when this routine + * was originally implemented in pcbios.S. + */ + in_stack.int_number = ( ( irq - 8 ) ^ 0x70 ) & 0x7f; + + RM_FRAGMENT(rm_fake_irq, + "popw %ax\n\t" /* %ax = INT number */ + "call 1f\n1:\tpop %bx\n\t" + "movb %al, %cs:(2f-1b+1)(%bx)\n\t" /* Overwrite INT number..*/ + "\n2:\tint $0x00\n\t" /* ..in this instruction */ + ); + + real_call ( rm_fake_irq, &in_stack, NULL ); +} + +/* Dump current 8259 status: enabled IRQs and handler addresses. + */ + +#ifdef DEBUG_IRQ +void dump_irq_status ( void ) { + int irq = 0; + + for ( irq = 0; irq < 16; irq++ ) { + if ( irq_enabled ( irq ) ) { + printf ( "IRQ%d enabled, ISR at %hx:%hx\n", irq, + IRQ_VECTOR(irq)->segment, + IRQ_VECTOR(irq)->offset ); + } + } +} +#endif diff --git a/src/arch/i386/core/prefixudata.lds b/src/arch/i386/core/prefixudata.lds new file mode 100644 index 00000000..1c76128e --- /dev/null +++ b/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/src/arch/i386/core/prefixzdata.lds b/src/arch/i386/core/prefixzdata.lds new file mode 100644 index 00000000..bf6ea977 --- /dev/null +++ b/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/src/arch/i386/core/pxe_callbacks.c b/src/arch/i386/core/pxe_callbacks.c new file mode 100644 index 00000000..344d34fe --- /dev/null +++ b/src/arch/i386/core/pxe_callbacks.c @@ -0,0 +1,364 @@ +/* PXE callback mechanisms. This file contains only the portions + * specific to i386: i.e. the low-level mechanisms for calling in from + * an NBP to the PXE stack and for starting an NBP from the PXE stack. + */ + +#ifdef PXE_EXPORT + +#include "etherboot.h" +#include "callbacks.h" +#include "realmode.h" +#include "pxe.h" +#include "pxe_callbacks.h" +#include "pxe_export.h" +#include "hidemem.h" +#include + +#define INSTALLED(x) ( (typeof(&x)) ( (void*)(&x) \ + - &pxe_callback_interface \ + + (void*)&pxe_stack->arch_data ) ) +#define pxe_intercept_int1a INSTALLED(_pxe_intercept_int1a) +#define pxe_intercepted_int1a INSTALLED(_pxe_intercepted_int1a) +#define pxe_pxenv_location INSTALLED(_pxe_pxenv_location) +#define INT1A_VECTOR ( (segoff_t*) ( phys_to_virt( 4 * 0x1a ) ) ) + +/* The overall size of the PXE stack is ( sizeof(pxe_stack_t) + + * pxe_callback_interface_size + rm_callback_interface_size ). + * Unfortunately, this isn't a compile-time constant, since + * {pxe,rm}_callback_interface_size depend on the length of the + * assembly code in these interfaces. + * + * We used to have a function pxe_stack_size() which returned this + * value. However, it actually needs to be a link-time constant, so + * that it can appear in the UNDIROMID structure in romprefix.S. We + * therefore export the three component sizes as absolute linker + * symbols, get the linker to add them together and generate a new + * absolute symbol _pxe_stack_size. We then import this value into a + * C variable pxe_stack_size, for access from C code. + */ + +/* gcc won't let us use extended asm outside a function (compiler + * bug), ao we have to put these asm statements inside a dummy + * function. + */ +static void work_around_gcc_bug ( void ) __attribute__ ((used)); +static void work_around_gcc_bug ( void ) { + /* Export sizeof(pxe_stack_t) as absolute linker symbol */ + __asm__ ( ".globl _pxe_stack_t_size" ); + __asm__ ( ".equ _pxe_stack_t_size, %c0" + : : "i" (sizeof(pxe_stack_t)) ); +} +/* Import _pxe_stack_size absolute linker symbol into C variable */ +extern int pxe_stack_size; +__asm__ ( "pxe_stack_size: .long _pxe_stack_size" ); + +/* Utility routine: byte checksum + */ +uint8_t byte_checksum ( void *address, size_t size ) { + unsigned int i, sum = 0; + + for ( i = 0; i < size; i++ ) { + sum += ((uint8_t*)address)[i]; + } + return (uint8_t)sum; +} + +/* install_pxe_stack(): install PXE stack. + * + * Use base = NULL for auto-allocation of base memory + * + * IMPORTANT: no further allocation of base memory should take place + * before the PXE stack is removed. This is to work around a small + * but important deficiency in the PXE specification. + */ +pxe_stack_t * install_pxe_stack ( void *base ) { + pxe_t *pxe; + pxenv_t *pxenv; + void *pxe_callback_code; + void (*pxe_in_call_far)(void); + void (*pxenv_in_call_far)(void); + void *rm_callback_code; + void *e820mangler_code; + void *end; + + /* If already installed, just return */ + if ( pxe_stack != NULL ) return pxe_stack; + + /* Allocate base memory if requested to do so + */ + if ( base == NULL ) { + base = allot_base_memory ( pxe_stack_size ); + if ( base == NULL ) return NULL; + } + + /* Round address up to 16-byte physical alignment */ + pxe_stack = (pxe_stack_t *) + ( phys_to_virt ( ( virt_to_phys(base) + 0xf ) & ~0xf ) ); + /* Zero out allocated stack */ + memset ( pxe_stack, 0, sizeof(*pxe_stack) ); + + /* Calculate addresses for portions of the stack */ + pxe = &(pxe_stack->pxe); + pxenv = &(pxe_stack->pxenv); + pxe_callback_code = &(pxe_stack->arch_data); + pxe_in_call_far = _pxe_in_call_far + + ( pxe_callback_code - &pxe_callback_interface ); + pxenv_in_call_far = _pxenv_in_call_far + + ( pxe_callback_code - &pxe_callback_interface ); + rm_callback_code = pxe_callback_code + pxe_callback_interface_size; + + e820mangler_code = (void*)(((int)rm_callback_code + + rm_callback_interface_size + 0xf ) & ~0xf); + end = e820mangler_code + e820mangler_size; + + /* Initialise !PXE data structures */ + memcpy ( pxe->Signature, "!PXE", 4 ); + pxe->StructLength = sizeof(*pxe); + pxe->StructRev = 0; + pxe->reserved_1 = 0; + /* We don't yet have an UNDI ROM ID structure */ + pxe->UNDIROMID.segment = 0; + pxe->UNDIROMID.offset = 0; + /* or a BC ROM ID structure */ + pxe->BaseROMID.segment = 0; + pxe->BaseROMID.offset = 0; + pxe->EntryPointSP.segment = SEGMENT(pxe_stack); + pxe->EntryPointSP.offset = (void*)pxe_in_call_far - (void*)pxe_stack; + /* No %esp-compatible entry point yet */ + pxe->EntryPointESP.segment = 0; + pxe->EntryPointESP.offset = 0; + pxe->StatusCallout.segment = -1; + pxe->StatusCallout.offset = -1; + pxe->reserved_2 = 0; + pxe->SegDescCn = 7; + pxe->FirstSelector = 0; + /* PXE specification doesn't say anything about when the stack + * space should get freed. We work around this by claiming it + * as our data segment as well. + */ + pxe->Stack.Seg_Addr = pxe->UNDIData.Seg_Addr = real_mode_stack >> 4; + pxe->Stack.Phy_Addr = pxe->UNDIData.Phy_Addr = real_mode_stack; + pxe->Stack.Seg_Size = pxe->UNDIData.Seg_Size = real_mode_stack_size; + /* Code segment has to be the one containing the data structures... */ + pxe->UNDICode.Seg_Addr = SEGMENT(pxe_stack); + pxe->UNDICode.Phy_Addr = virt_to_phys(pxe_stack); + pxe->UNDICode.Seg_Size = end - (void*)pxe_stack; + /* No base code loaded */ + pxe->BC_Data.Seg_Addr = 0; + pxe->BC_Data.Phy_Addr = 0; + pxe->BC_Data.Seg_Size = 0; + pxe->BC_Code.Seg_Addr = 0; + pxe->BC_Code.Phy_Addr = 0; + pxe->BC_Code.Seg_Size = 0; + pxe->BC_CodeWrite.Seg_Addr = 0; + pxe->BC_CodeWrite.Phy_Addr = 0; + pxe->BC_CodeWrite.Seg_Size = 0; + pxe->StructCksum -= byte_checksum ( pxe, sizeof(*pxe) ); + + /* Initialise PXENV+ data structures */ + memcpy ( pxenv->Signature, "PXENV+", 6 ); + pxenv->Version = 0x201; + pxenv->Length = sizeof(*pxenv); + pxenv->RMEntry.segment = SEGMENT(pxe_stack); + pxenv->RMEntry.offset = (void*)pxenv_in_call_far - (void*)pxe_stack; + pxenv->PMOffset = 0; /* "Do not use" says the PXE spec */ + pxenv->PMSelector = 0; /* "Do not use" says the PXE spec */ + pxenv->StackSeg = pxenv->UNDIDataSeg = real_mode_stack >> 4; + pxenv->StackSize = pxenv->UNDIDataSize = real_mode_stack_size; + pxenv->BC_CodeSeg = 0; + pxenv->BC_CodeSize = 0; + pxenv->BC_DataSeg = 0; + pxenv->BC_DataSize = 0; + /* UNDIData{Seg,Size} set above */ + pxenv->UNDICodeSeg = SEGMENT(pxe_stack); + pxenv->UNDICodeSize = end - (void*)pxe_stack; + pxenv->PXEPtr.segment = SEGMENT(pxe); + pxenv->PXEPtr.offset = OFFSET(pxe); + pxenv->Checksum -= byte_checksum ( pxenv, sizeof(*pxenv) ); + + /* Mark stack as inactive */ + pxe_stack->state = CAN_UNLOAD; + + /* Install PXE and RM callback code and E820 mangler */ + memcpy ( pxe_callback_code, &pxe_callback_interface, + pxe_callback_interface_size ); + install_rm_callback_interface ( rm_callback_code, 0 ); + install_e820mangler ( e820mangler_code ); + + return pxe_stack; +} + +/* Use the UNDI data segment as our real-mode stack. This is for when + * we have been loaded via the UNDI loader + */ +void use_undi_ds_for_rm_stack ( uint16_t ds ) { + forget_real_mode_stack(); + real_mode_stack = virt_to_phys ( VIRTUAL ( ds, 0 ) ); + lock_real_mode_stack = 1; +} + +/* Activate PXE stack (i.e. hook interrupt vectors). The PXE stack + * *can* be used before it is activated, but it really shoudln't. + */ +int hook_pxe_stack ( void ) { + if ( pxe_stack == NULL ) return 0; + if ( pxe_stack->state >= MIDWAY ) return 1; + + /* Hook INT15 handler */ + hide_etherboot(); + + /* Hook INT1A handler */ + *pxe_intercepted_int1a = *INT1A_VECTOR; + pxe_pxenv_location->segment = SEGMENT(pxe_stack); + pxe_pxenv_location->offset = (void*)&pxe_stack->pxenv + - (void*)pxe_stack; + INT1A_VECTOR->segment = SEGMENT(&pxe_stack->arch_data); + INT1A_VECTOR->offset = (void*)pxe_intercept_int1a + - (void*)&pxe_stack->arch_data; + + /* Mark stack as active */ + pxe_stack->state = MIDWAY; + return 1; +} + +/* Deactivate the PXE stack (i.e. unhook interrupt vectors). + */ +int unhook_pxe_stack ( void ) { + if ( pxe_stack == NULL ) return 0; + if ( pxe_stack->state <= CAN_UNLOAD ) return 1; + + /* Restore original INT15 and INT1A handlers */ + *INT1A_VECTOR = *pxe_intercepted_int1a; + if ( !unhide_etherboot() ) { + /* Cannot unhook INT15. We're up the creek without + * even a suitable log out of which to fashion a + * paddle. There are some very badly behaved NBPs + * that will ignore plaintive pleas such as + * PXENV_KEEP_UNDI and just zero out our code anyway. + * This means they end up vapourising an active INT15 + * handler, which is generally not a good thing to do. + */ + return 0; + } + + /* Mark stack as inactive */ + pxe_stack->state = CAN_UNLOAD; + return 1; +} + +/* remove_pxe_stack(): remove PXE stack installed by install_pxe_stack() + */ +void remove_pxe_stack ( void ) { + /* Ensure stack is deactivated, then free up the memory */ + if ( ensure_pxe_state ( CAN_UNLOAD ) ) { + forget_base_memory ( pxe_stack, pxe_stack_size ); + pxe_stack = NULL; + } else { + printf ( "Cannot remove PXE stack!\n" ); + } +} + +/* xstartpxe(): start up a PXE image + */ +int xstartpxe ( void ) { + int nbp_exit; + struct { + reg16_t bx; + reg16_t es; + segoff_t pxe; + } PACKED in_stack; + + /* Set up registers and stack parameters to pass to PXE NBP */ + in_stack.es.word = SEGMENT(&(pxe_stack->pxenv)); + in_stack.bx.word = OFFSET(&(pxe_stack->pxenv)); + in_stack.pxe.segment = SEGMENT(&(pxe_stack->pxe)); + in_stack.pxe.offset = OFFSET(&(pxe_stack->pxe)); + + /* Real-mode trampoline fragment used to jump to PXE NBP + */ + RM_FRAGMENT(jump_to_pxe_nbp, + "popw %bx\n\t" + "popw %es\n\t" + "lcall $" RM_STR(PXE_LOAD_SEGMENT) ", $" RM_STR(PXE_LOAD_OFFSET) "\n\t" + ); + + /* Call to PXE image */ + gateA20_unset(); + nbp_exit = real_call ( jump_to_pxe_nbp, &in_stack, NULL ); + gateA20_set(); + + return nbp_exit; +} + +int pxe_in_call ( in_call_data_t *in_call_data, va_list params ) { + /* i386 calling conventions; the only two defined by Intel's + * PXE spec. + * + * Assembly code must pass a long containing the PXE version + * code (i.e. 0x201 for !PXE, 0x200 for PXENV+) as the first + * parameter after the in_call opcode. This is used to decide + * whether to take parameters from the stack (!PXE) or from + * registers (PXENV+). + */ + uint32_t api_version = va_arg ( params, typeof(api_version) ); + uint16_t opcode; + segoff_t segoff; + t_PXENV_ANY *structure; + + if ( api_version >= 0x201 ) { + /* !PXE calling convention */ + pxe_call_params_t pxe_params + = va_arg ( params, typeof(pxe_params) ); + opcode = pxe_params.opcode; + segoff = pxe_params.segoff; + } else { + /* PXENV+ calling convention */ + opcode = in_call_data->pm->regs.bx; + segoff.segment = in_call_data->rm->seg_regs.es; + segoff.offset = in_call_data->pm->regs.di; + } + structure = VIRTUAL ( segoff.segment, segoff.offset ); + return pxe_api_call ( opcode, structure ); +} + +#ifdef TEST_EXCLUDE_ALGORITHM +/* This code retained because it's a difficult algorithm to tweak with + * confidence + */ +int ___test_exclude ( int start, int len, int estart, int elen, int fixbase ); +void __test_exclude ( int start, int len, int estart, int elen, int fixbase ) { + int newrange = ___test_exclude ( start, len, estart, elen, fixbase ); + int newstart = ( newrange >> 16 ) & 0xffff; + int newlen = ( newrange & 0xffff ); + + printf ( "[%x,%x): excluding [%x,%x) %s gives [%x,%x)\n", + start, start + len, + estart, estart + elen, + ( fixbase == 0 ) ? " " : "fb", + newstart, newstart + newlen ); +} +void _test_exclude ( int start, int len, int estart, int elen ) { + __test_exclude ( start, len, estart, elen, 0 ); + __test_exclude ( start, len, estart, elen, 1 ); +} +void test_exclude ( void ) { + _test_exclude ( 0x8000, 0x1000, 0x0400, 0x200 ); /* before */ + _test_exclude ( 0x8000, 0x1000, 0x9000, 0x200 ); /* after */ + _test_exclude ( 0x8000, 0x1000, 0x7f00, 0x200 ); /* before overlap */ + _test_exclude ( 0x8000, 0x1000, 0x8f00, 0x200 ); /* after overlap */ + _test_exclude ( 0x8000, 0x1000, 0x8000, 0x200 ); /* align start */ + _test_exclude ( 0x8000, 0x1000, 0x8e00, 0x200 ); /* align end */ + _test_exclude ( 0x8000, 0x1000, 0x8100, 0x200 ); /* early overlap */ + _test_exclude ( 0x8000, 0x1000, 0x8d00, 0x200 ); /* late overlap */ + _test_exclude ( 0x8000, 0x1000, 0x7000, 0x3000 ); /* total overlap */ + _test_exclude ( 0x8000, 0x1000, 0x8000, 0x1000 ); /* exact overlap */ +} +#endif /* TEST_EXCLUDE_ALGORITHM */ + +#else /* PXE_EXPORT */ + +/* Define symbols used by the linker scripts, to prevent link errors */ +__asm__ ( ".globl _pxe_stack_t_size" ); +__asm__ ( ".equ _pxe_stack_t_size, 0" ); + +#endif /* PXE_EXPORT */ diff --git a/src/arch/i386/core/pxe_loader.c b/src/arch/i386/core/pxe_loader.c new file mode 100644 index 00000000..1b611891 --- /dev/null +++ b/src/arch/i386/core/pxe_loader.c @@ -0,0 +1,94 @@ +/* + * PXE image loader for Etherboot. + * + * Note: There is no signature check for PXE images because there is + * no signature. Well done, Intel! Consequently, pxe_probe() must be + * called last of all the image_probe() routines, because it will + * *always* claim the image. + */ + +#ifndef PXE_EXPORT +#error PXE_IMAGE requires PXE_EXPORT +#endif + +#include "etherboot.h" +#include "pxe_callbacks.h" +#include "pxe_export.h" +#include "pxe.h" + +unsigned long pxe_load_offset; + +static sector_t pxe_download ( unsigned char *data, + unsigned int len, int eof ); + +static inline os_download_t pxe_probe ( unsigned char *data __unused, + unsigned int len __unused ) { + printf("(PXE)"); + pxe_load_offset = 0; + return pxe_download; +} + +static sector_t pxe_download ( unsigned char *data, + unsigned int len, int eof ) { + unsigned long block_address = PXE_LOAD_ADDRESS + pxe_load_offset; + PXENV_STATUS_t nbp_exit; + + /* Check segment will fit. We can't do this in probe() + * because there's nothing in the non-existent header to tell + * us how long the image is. + */ + if ( ! prep_segment ( block_address, block_address + len, + block_address + len, + pxe_load_offset, pxe_load_offset + len ) ) { + longjmp ( restart_etherboot, -2 ); + } + + /* Load block into memory, continue loading until eof */ + memcpy ( phys_to_virt ( block_address ), data, len ); + pxe_load_offset += len; + if ( ! eof ) { + return 0; + } + + /* Start up PXE NBP */ + done ( 0 ); + + /* Install and activate a PXE stack */ + pxe_stack = install_pxe_stack ( NULL ); + if ( ensure_pxe_state ( READY ) ) { + /* Invoke the NBP */ + nbp_exit = xstartpxe(); + } else { + /* Fake success so we tear down the stack */ + nbp_exit = PXENV_STATUS_SUCCESS; + } + + /* NBP has three exit codes: + * PXENV_STATUS_KEEP_UNDI : keep UNDI and boot next device + * PXENV_STATUS_KEEP_ALL : keep all and boot next device + * anything else : remove all and boot next device + * + * Strictly, we're meant to hand back to the BIOS, but this + * would prevent the useful combination of "PXE NBP fails, so + * let Etherboot try to boot its next device". We therefore + * take liberties. + */ + if ( nbp_exit != PXENV_STATUS_KEEP_UNDI && + nbp_exit != PXENV_STATUS_KEEP_ALL ) { + /* Tear down PXE stack */ + remove_pxe_stack(); + } + + /* Boot next device. Under strict PXE compliance, exit back + * to the BIOS, otherwise let Etherboot move to the next + * device. + */ +#ifdef PXE_STRICT + longjmp ( restart_etherboot, 255 ); +#else + longjmp ( restart_etherboot, 4 ); +#endif + + /* Never reached; avoid compiler warning */ + return ( 0 ); +} diff --git a/src/arch/i386/core/realmode.c b/src/arch/i386/core/realmode.c new file mode 100644 index 00000000..ef4ede86 --- /dev/null +++ b/src/arch/i386/core/realmode.c @@ -0,0 +1,148 @@ +/* Real-mode interface: C portions. + * + * Initial version by Michael Brown , January 2004. + */ + +#include "etherboot.h" +#include "realmode.h" +#include "segoff.h" + +#define RM_STACK_SIZE ( 0x1000 ) + +/* gcc won't let us use extended asm outside a function (compiler + * bug), ao we have to put these asm statements inside a dummy + * function. + */ +static void work_around_gcc_bug ( void ) __attribute__ ((used)); +static void work_around_gcc_bug ( void ) { + /* Export _real_mode_stack_size as absolute linker symbol */ + __asm__ ( ".globl _real_mode_stack_size" ); + __asm__ ( ".equ _real_mode_stack_size, %c0" : : "i" (RM_STACK_SIZE) ); +} + +/* While Etherboot remains in base memory the real-mode stack is + * placed in the Etherboot main stack. The first allocation or + * deallocation of base memory will cause a 'proper' real-mode stack + * to be allocated. This will happen before Etherboot is relocated to + * high memory. + */ +uint32_t real_mode_stack = 0; +size_t real_mode_stack_size = RM_STACK_SIZE; +int lock_real_mode_stack = 0; /* Set to make stack immobile */ + +/* Make a call to a real-mode code block. + */ + +/* These is the structure that exists on the stack as the paramters + * passed in to _real_call. We pass a pointer to this struct to + * prepare_real_call(), to save stack space. + */ +typedef struct { + void *fragment; + int fragment_len; + void *in_stack; + int in_stack_len; + void *out_stack; + int out_stack_len; +} real_call_params_t; + +uint32_t prepare_real_call ( real_call_params_t *p, + int local_stack_len, char *local_stack ) { + char *stack_base; + char *stack_end; + char *stack; + char *s; + prot_to_real_params_t *p2r_params; + real_to_prot_params_t *r2p_params; + + /* Work out where we're putting the stack */ + if ( virt_to_phys(local_stack) < 0xa0000 ) { + /* Current stack is in base memory. We can therefore + * use it directly, with a constraint on the size that + * we don't know; assume that we can use up to + * real_mode_stack_size. (Not a valid assumption, but + * it will do). + */ + stack_end = local_stack + local_stack_len; + stack_base = stack_end - real_mode_stack_size; + } else { + if (!real_mode_stack) { + allot_real_mode_stack(); + } + /* Use the allocated real-mode stack in base memory. + * This has fixed start and end points. + */ + stack_base = phys_to_virt(real_mode_stack); + stack_end = stack_base + real_mode_stack_size; + } + s = stack = stack_end - local_stack_len; + + /* Compile input stack and trampoline code to stack */ + if ( p->in_stack_len ) { + memcpy ( s, p->in_stack, p->in_stack_len ); + s += p->in_stack_len; + } + memcpy ( s, _prot_to_real_prefix, prot_to_real_prefix_size ); + s += prot_to_real_prefix_size; + p2r_params = (prot_to_real_params_t*) ( s - sizeof(*p2r_params) ); + memcpy ( s, p->fragment, p->fragment_len ); + s += p->fragment_len; + memcpy ( s, _real_to_prot_suffix, real_to_prot_suffix_size ); + s += real_to_prot_suffix_size; + r2p_params = (real_to_prot_params_t*) ( s - sizeof(*r2p_params) ); + + /* Set parameters within compiled stack */ + p2r_params->ss = p2r_params->cs = SEGMENT ( stack_base ); + p2r_params->esp = virt_to_phys ( stack ); + p2r_params->r2p_params = virt_to_phys ( r2p_params ); + r2p_params->out_stack = ( p->out_stack == NULL ) ? + 0 : virt_to_phys ( p->out_stack ); + r2p_params->out_stack_len = p->out_stack_len; + + return virt_to_phys ( stack + p->in_stack_len ); +} + + +/* Parameters are not genuinely unused; they are passed to + * prepare_real_call() as part of a real_call_params_t struct. + */ +uint16_t _real_call ( void *fragment, int fragment_len, + void *in_stack __unused, int in_stack_len, + void *out_stack __unused, int out_stack_len __unused ) { + uint16_t retval; + + /* This code is basically equivalent to + * + * uint32_t trampoline; + * char local_stack[ in_stack_len + prot_to_real_prefix_size + + * fragment_len + real_to_prot_suffix_size ]; + * trampoline = prepare_real_call ( &fragment, local_stack ); + * __asm__ ( "call _virt_to_phys\n\t" + * "call %%eax\n\t" + * "call _phys_to_virt\n\t" + * : "=a" (retval) : "0" (trampoline) ); + * + * but implemented in assembly to avoid problems with not + * being certain exactly how gcc handles %esp. + */ + + __asm__ ( "pushl %%ebp\n\t" + "movl %%esp, %%ebp\n\t" /* %esp preserved via %ebp */ + "subl %%ecx, %%esp\n\t" /* space for inline RM stack */ + "pushl %%esp\n\t" /* set up RM stack */ + "pushl %%ecx\n\t" + "pushl %%eax\n\t" + "call prepare_real_call\n\t" /* %eax = trampoline addr */ + "addl $12, %%esp\n\t" + "call _virt_to_phys\n\t" /* switch to phys addr */ + "call *%%eax\n\t" /* call to trampoline */ + "call _phys_to_virt\n\t" /* switch to virt addr */ + "movl %%ebp, %%esp\n\t" /* restore %esp & %ebp */ + "popl %%ebp\n\t" + : "=a" ( retval ) + : "0" ( &fragment ) + , "c" ( ( ( in_stack_len + prot_to_real_prefix_size + + fragment_len + real_to_prot_suffix_size ) + + 0x3 ) & ~0x3 ) ); + return retval; +} diff --git a/src/arch/i386/core/realmode_asm.S b/src/arch/i386/core/realmode_asm.S new file mode 100644 index 00000000..28a5bfed --- /dev/null +++ b/src/arch/i386/core/realmode_asm.S @@ -0,0 +1,695 @@ +/* Real-mode interface: assembly-language portions. + * + * Initial version by Michael Brown , January 2004. + */ + +#include "realmode.h" +#include "callbacks.h" + +#if 1 /* CODE16 */ + +#define BOCHSBP xchgw %bx,%bx + +#define NUM_PUSHA_REGS (8) +#define NUM_SEG_REGS (6) + + .text + .arch i386 + .section ".text16.nocompress", "ax", @progbits + .code16 + + .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 + +/**************************************************************************** + * REAL-MODE CALLBACK INTERFACE + * + * This must be copied down to base memory in order for external + * programs to be able to make calls in to Etherboot. Store the + * current physical address of Etherboot (i.e. virt_to_phys(_text)) in + * (uint32_t)rm_etherboot_location, then copy + * (uint16_t)rm_callback_interface_size bytes starting at + * &((void)rm_callback_interface). + * + * There are two defined entry points: + * Offset RM_IN_CALL = 0 Near call entry point + * Offset RM_IN_CALL_FAR = 2 Far call entry point + * + * Note that the routines _prot_to_real and _real_to_prot double as + * trampoline fragments for external calls (calls from Etherboot to + * real-mode code). _prot_to_real does not automatically re-enable + * interrupts; this is to allow for the potential of using Etherboot + * code as an ISR. _real_to_prot does automatically disable + * interrupts, since we don't have a protected-mode IDT. + **************************************************************************** + */ + + .globl rm_callback_interface + .code16 +rm_callback_interface: + .globl _rm_in_call +_rm_in_call: + jmp _real_in_call + .globl _rm_in_call_far +_rm_in_call_far: + jmp _real_in_call_far + +/**************************************************************************** + * _real_in_call + * + * Parameters: + * 16-bit real-mode near/far return address (implicit from [l]call + * to routine) Other parameters as for _in_call_far(). + * + * This routine will convert the 16-bit real-mode far return address + * to a 32-bit real-mode far return address, switch to protected mode + * using _real_to_prot and call in to _in_call_far. + **************************************************************************** + */ + +#define RIC_PRESERVE ( 8 ) +#define RIC_OFFSET_CALLADDR ( RIC_PRESERVE ) +#define RIC_OFFSET_CALLADDR_E ( RIC_OFFSET_CALLADDR + 4 ) +#define RIC_OFFSET_CONTADDR ( RIC_OFFSET_CALLADDR_E ) +#define RIC_OFFSET_CONTADDR_E ( RIC_OFFSET_CONTADDR + 4 ) +#define RIC_OFFSET_OPCODE ( RIC_OFFSET_CONTADDR_E ) +#define RIC_OFFSET_OPCODE_E ( RIC_OFFSET_OPCODE + 4 ) +#define RIC_OFFSET_SEG_REGS ( RIC_OFFSET_OPCODE_E ) +#define RIC_OFFSET_SEG_REGS_E ( RIC_OFFSET_SEG_REGS + ( NUM_SEG_REGS * 2 ) ) +#define RIC_OFFSET_PAD ( RIC_OFFSET_SEG_REGS_E ) +#define RIC_OFFSET_PAD_E ( RIC_OFFSET_PAD + 2 ) +#define RIC_OFFSET_FLAGS ( RIC_OFFSET_PAD_E ) +#define RIC_OFFSET_FLAGS_E ( RIC_OFFSET_FLAGS + 2 ) +#define RIC_OFFSET_RETADDR ( RIC_OFFSET_FLAGS_E ) +#define RIC_OFFSET_RETADDR_E ( RIC_OFFSET_RETADDR + 4 ) +#define RIC_OFFSET_ORIG_OPCODE ( RIC_OFFSET_RETADDR_E ) +#define RIC_INSERT_LENGTH ( RIC_OFFSET_OPCODE_E - RIC_OFFSET_CALLADDR ) + + .code16 +_real_in_call: + /* Expand near return address to far return address + */ + pushw %ax /* Extend stack, store %ax */ + pushfw + pushw %bp + movw %sp, %bp + movw %cs, %ax + xchgw %ax, 6(%bp) + xchgw %ax, 4(%bp) /* also restores %ax */ + popw %bp + popfw + /* Fall through to _real_in_call_far */ + +_real_in_call_far: + /* Store flags and pad */ + pushfw + pushw %ax + + /* Store segment registers. Order matches that of seg_regs_t */ + pushw %gs + pushw %fs + pushw %es + pushw %ds + pushw %ss + pushw %cs + + /* Switch to protected mode */ + call _real_to_prot + .code32 + + /* Allow space for expanded stack */ + subl $RIC_INSERT_LENGTH, %esp + + /* Store temporary registers */ + pushl %ebp + pushl %eax + + /* Copy opcode, set EB_CALL_FROM_REAL_MODE and EP_SKIP_OPCODE. + * Copy it because _in_call() and i386_in_call() expect it at + * a fixed position, not as part of the va_list. + */ + movl RIC_OFFSET_ORIG_OPCODE(%esp), %eax + orl $(EB_CALL_FROM_REAL_MODE|EB_SKIP_OPCODE), %eax + movl %eax, RIC_OFFSET_OPCODE(%esp) + + /* Set up call and return addresses */ + call 1f +1: popl %ebp + subl $1b, %ebp /* %ebp = offset */ + movl rm_etherboot_location(%ebp), %eax /* Etherboot phys addr */ + subl $_text, %eax + addl $_in_call, %eax /* _in_call phys addr */ + movl %eax, RIC_OFFSET_CALLADDR(%esp) + leal 2f(%ebp), %eax /* continuation address */ + movl %eax, RIC_OFFSET_CONTADDR(%esp) + + /* Restore temporary registers */ + popl %eax + popl %ebp + + /* Call to _in_call */ + ret + /* opcode will be popped automatically thanks to EB_SKIP_OPCODE */ + +2: /* Continuation point */ + call _prot_to_real /* Return to real mode */ + /* Note: the first two words of our segment register store + * happens to be exactly what we need to pass as parameters to + * _prot_to_real. + */ + .code16 + popw %ds /* Restore segment registers */ + popw %ds /* (skip cs&ss since these */ + popw %ds /* have already been set by */ + popw %es /* _prot_to_real */ + popw %fs + popw %gs + addw $2, %sp /* skip pad */ + + /* Check for EB_SKIP_OPCODE */ + pushw %bp + movw %sp, %bp + testl $EB_SKIP_OPCODE, 6(%bp) + popw %bp + jnz 1f + /* Normal return */ + popfw /* Restore interrupt status */ + lret /* Back to caller */ +1: /* Return and skip opcode */ + popfw + lret $4 + +/**************************************************************************** + * rm_etherboot_location: the current physical location of Etherboot. + * Needed so that real-mode callback routines can locate Etherboot. + **************************************************************************** + */ + .globl rm_etherboot_location +rm_etherboot_location: .long 0 + +/**************************************************************************** + * _prot_to_real_prefix + * + * Trampoline fragment. Switch from 32-bit protected mode with flat + * physical addresses to 16-bit real mode. Store registers in the + * trampoline for restoration by _real_to_prot_suffix. Switch to + * stack in base memory. + **************************************************************************** + */ + + .globl _prot_to_real_prefix + .code32 +_prot_to_real_prefix: + /* Registers to preserve */ + pushl %ebx + pushl %esi + pushl %edi + pushl %ebp + + /* Calculate offset */ + call 1f +1: popl %ebp + subl $1b, %ebp /* %ebp = offset for labels in p2r*/ + + /* Preserve registers and return address in r2p_params */ + movl p2r_r2p_params(%ebp), %ebx + subl $r2p_params, %ebx /* %ebx = offset for labels in r2p */ + popl r2p_ebp(%ebx) + popl r2p_edi(%ebx) + popl r2p_esi(%ebx) + popl r2p_ebx(%ebx) + popl r2p_ret_addr(%ebx) + movl %esp, r2p_esp(%ebx) + + /* Switch stacks */ + movl p2r_esp(%ebp), %esp + + /* Switch to real mode */ + pushl p2r_segments(%ebp) + call _prot_to_real + .code16 + addw $4, %sp + + /* Fall through to next trampoline fragment */ + jmp _prot_to_real_prefix_end + +/**************************************************************************** + * _prot_to_real + * + * Switch from 32-bit protected mode with flat physical addresses to + * 16-bit real mode. Stack and code must be in base memory when + * called. %cs, %ss, %eip, %esp are changed to real-mode values, + * other segment registers are destroyed, all other registers are + * preserved. Interrupts are *not* enabled. + * + * Parameters: + * %cs Real-mode code segment (word) + * %ss Real-mode stack segment (word) + **************************************************************************** + */ + +#define P2R_PRESERVE ( 12 ) +#define P2R_OFFSET_RETADDR ( P2R_PRESERVE ) +#define P2R_OFFSET_RETADDR_E ( P2R_OFFSET_RETADDR + 4 ) +#define P2R_OFFSET_CS ( P2R_OFFSET_RETADDR_E ) +#define P2R_OFFSET_CS_E ( P2R_OFFSET_CS + 2 ) +#define P2R_OFFSET_SS ( P2R_OFFSET_CS_E ) +#define P2R_OFFSET_SS_E ( P2R_OFFSET_SS + 2 ) + + .globl _prot_to_real + .code32 +_prot_to_real: + /* Preserve registers */ + pushl %ebp + pushl %ebx + pushl %eax + + /* Calculate offset */ + call 1f +1: popl %ebp + subl $1b, %ebp /* %ebp = offset for labels in p2r*/ + + /* Set up GDT with real-mode limits and appropriate bases for + * real-mode %cs and %ss. Set up protected-mode continuation + * point on stack. + */ + /* Fixup GDT */ + leal p2r_gdt(%ebp), %eax + movl %eax, p2r_gdt_addr(%ebp) + + /* Calculate CS base address: set GDT code segment, adjust + * return address, set up continuation address on stack. + */ + movzwl P2R_OFFSET_CS(%esp), %eax + shll $4, %eax + /* Change return address to real-mode far address */ + subl %eax, P2R_OFFSET_RETADDR(%esp) + movl %eax, %ebx + shrl $4, %ebx + movw %bx, (P2R_OFFSET_RETADDR+2)(%esp) + /* First real mode address */ + movl %eax, %ebx + shrl $4, %ebx + pushw %bx + leal 3f(%ebp), %ebx + subl %eax, %ebx + pushw %bx + /* Continuation address */ + pushl $(p2r_rmcs - p2r_gdt) + leal 2f(%ebp), %ebx + subl %eax, %ebx + pushl %ebx + /* Code segment in GDT */ + movw %ax, (p2r_rmcs+2)(%ebp) + shrl $16, %eax /* Remainder of cs base addr */ + movb %al, (p2r_rmcs+4)(%ebp) + movb %ah, (p2r_rmcs+7)(%ebp) + + /* Calculate SS base address: set GDT data segment, retain to + * use for adjusting %esp. + */ + movzwl (12+P2R_OFFSET_SS)(%esp), %eax /* Set ss base address */ + shll $4, %eax + movw %ax, (p2r_rmds+2)(%ebp) + movl %eax, %ebx + shrl $16, %ebx + movb %bl, (p2r_rmds+4)(%ebp) + movb %bh, (p2r_rmds+7)(%ebp) + + /* Load GDT */ + lgdt p2r_gdt(%ebp) + /* Reload all segment registers and adjust %esp */ + movw $(p2r_rmds - p2r_gdt), %bx /* Pmode DS */ + movw %bx, %ss + subl %eax, %esp /* %esp now less than 0x10000 */ + movw %bx, %ds + movw %bx, %es + movw %bx, %fs + movw %bx, %gs + lret /* %cs:eip */ +2: /* Segment registers now have 16-bit limits. */ + .code16 + + /* Switch to real mode */ + movl %cr0, %ebx + andb $0!CR0_PE, %bl + movl %ebx, %cr0 + + /* Make intersegment jmp to flush the processor pipeline + * and reload %cs:%eip (to clear upper 16 bits of %eip). + */ + lret +3: + + /* Load real-mode segment value to %ss. %sp already OK */ + shrl $4, %eax + movw %ax, %ss + + /* Restore registers */ + popl %eax + popl %ebx + popl %ebp + + /* Return to caller in real-mode */ + lret + +#ifdef FLATTEN_REAL_MODE +#define RM_LIMIT_16_19__AVL__SIZE__GRANULARITY 0x8f +#else +#define RM_LIMIT_16_19__AVL__SIZE__GRANULARITY 0x00 +#endif + +p2r_gdt: +p2r_gdtarg: +p2r_gdt_limit: .word p2r_gdt_end - p2r_gdt - 1 +p2r_gdt_addr: .long 0 +p2r_gdt_padding: .word 0 +p2r_rmcs: + /* 16 bit real mode code segment */ + .word 0xffff,(0&0xffff) + .byte (0>>16),0x9b,RM_LIMIT_16_19__AVL__SIZE__GRANULARITY,(0>>24) +p2r_rmds: + /* 16 bit real mode data segment */ + .word 0xffff,(0&0xffff) + .byte (0>>16),0x93,RM_LIMIT_16_19__AVL__SIZE__GRANULARITY,(0>>24) +p2r_gdt_end: + + /* This is the end of the trampoline prefix code. When used + * as a prefix, fall through to the following code in the + * trampoline. + */ +p2r_params: /* Structure must match prot_to_real_params_t in realmode.h */ +p2r_esp: .long 0 +p2r_segments: +p2r_cs: .word 0 +p2r_ss: .word 0 +p2r_r2p_params: .long 0 + .globl _prot_to_real_prefix_end +_prot_to_real_prefix_end: + + .globl _prot_to_real_prefix_size + .equ _prot_to_real_prefix_size, _prot_to_real_prefix_end - _prot_to_real_prefix + .globl prot_to_real_prefix_size +prot_to_real_prefix_size: + .word _prot_to_real_prefix_size + +/**************************************************************************** + * _real_to_prot_suffix + * + * Trampoline fragment. Switch from 16-bit real-mode to 32-bit + * protected mode with flat physical addresses. Copy returned stack + * parameters to output_stack. Restore registers preserved by + * _prot_to_real_prefix. Restore stack to previous location. + **************************************************************************** + */ + + .globl _real_to_prot_suffix + .code16 +_real_to_prot_suffix: + + /* Switch to protected mode */ + call _real_to_prot + .code32 + + /* Calculate offset */ + call 1f +1: popl %ebp + subl $1b, %ebp /* %ebp = offset for labels in r2p */ + + /* Copy stack to out_stack */ + movl r2p_out_stack(%ebp), %edi + movl r2p_out_stack_len(%ebp), %ecx + movl %esp, %esi + cld + rep movsb + + /* Switch back to original stack */ + movl r2p_esp(%ebp), %esp + + /* Restore registers and return */ + pushl r2p_ret_addr(%ebp) /* Set up return address on stack */ + movl r2p_ebx(%ebp), %ebx + movl r2p_esi(%ebp), %esi + movl r2p_edi(%ebp), %edi + movl r2p_ebp(%ebp), %ebp + ret + +/**************************************************************************** + * _real_to_prot + * + * Switch from 16-bit real-mode to 32-bit protected mode with flat + * physical addresses. All segment registers are destroyed, %eip and + * %esp are changed to flat physical values, all other registers are + * preserved. Interrupts are disabled. + * + * Parameters: none + **************************************************************************** + */ + +#define R2P_PRESERVE ( 12 ) +#define R2P_OFFSET_RETADDR ( R2P_PRESERVE ) +#define R2P_OFFSET_ORIG_RETADDR ( R2P_OFFSET_RETADDR + 2 ) + + .globl _real_to_prot + .code16 +_real_to_prot: + /* Disable interrupts */ + cli + /* zero extend the return address */ + pushw $0 + + /* Preserve registers */ + pushl %ebp + pushl %ebx + pushl %eax + + /* Convert 16-bit real-mode near return address to + * 32-bit pmode physical near return address + */ + movw %sp, %bp + xorl %ebx, %ebx + push %cs + popw %bx + movw %bx, %ds + shll $4, %ebx + movzwl %ss:R2P_OFFSET_ORIG_RETADDR(%bp), %eax + addl %ebx, %eax + movl %eax, %ss:(R2P_OFFSET_RETADDR)(%bp) + + /* Store the code segment physical base address in %ebp */ + movl %ebx, %ebp + + /* Find the offset within the code segment that I am running at */ + xorl %ebx, %ebx + call 1f +1: popw %bx + + /* Set up GDT */ + leal (r2p_gdt-1b)(%bx), %eax /* %ds:ebx = %ds:bx = &(r2p_gdt) */ + addl %ebp, %eax /* %eax = &r2p_gdt (physical) */ + movl %eax, %ds:(r2p_gdt-1b+2)(%bx) /* Set phys. addr. in r2p_gdt */ + + /* Compute the first protected mode physical address */ + leal (2f-1b)(%bx), %eax + addl %ebp, %eax + movl %eax, %ds:(r2p_paddr-1b)(%bx) + + /* Calculate new %esp */ + xorl %eax, %eax + push %ss + popw %ax + shll $4, %eax + movzwl %sp, %ebp + addl %eax, %ebp /* %ebp = new %esp */ + + /* Load GDT */ + DATA32 lgdt %ds:(r2p_gdt-1b)(%bx) /* Load GDT */ + + /* Switch to protected mode */ + movl %cr0, %eax + orb $CR0_PE, %al + movl %eax, %cr0 + + /* flush prefetch queue, and reload %cs:%eip */ + DATA32 ljmp %ds:(r2p_paddr-1b)(%bx) + .code32 +2: + + /* Load segment registers, adjust %esp */ + movw $(r2p_pmds-r2p_gdt), %ax + movw %ax, %ss + movl %ebp, %esp + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + + /* Restore registers */ + popl %eax + popl %ebx + popl %ebp + + /* return to caller */ + ret + +r2p_gdt: + .word r2p_gdt_end - r2p_gdt - 1 /* limit */ + .long 0 /* addr */ + .word 0 +r2p_pmcs: + /* 32 bit protected mode code segment, physical addresses */ + .word 0xffff, 0 + .byte 0, 0x9f, 0xcf, 0 +r2p_pmds: + /* 32 bit protected mode data segment, physical addresses */ + .word 0xffff,0 + .byte 0,0x93,0xcf,0 +r2p_gdt_end: + +r2p_paddr: + .long 2b + .word r2p_pmcs - r2p_gdt, 0 + + + /* This is the end of the trampoline suffix code. + */ +r2p_params: /* Structure must match real_to_prot_params_t in realmode.h */ +r2p_ret_addr: .long 0 +r2p_esp: .long 0 +r2p_ebx: .long 0 +r2p_esi: .long 0 +r2p_edi: .long 0 +r2p_ebp: .long 0 +r2p_out_stack: .long 0 +r2p_out_stack_len: .long 0 + .globl _real_to_prot_suffix_end +_real_to_prot_suffix_end: + + .globl _real_to_prot_suffix_size + .equ _real_to_prot_suffix_size, _real_to_prot_suffix_end - _real_to_prot_suffix + .globl real_to_prot_suffix_size +real_to_prot_suffix_size: + .word _real_to_prot_suffix_size + +rm_callback_interface_end: + + .globl _rm_callback_interface_size + .equ _rm_callback_interface_size, rm_callback_interface_end - rm_callback_interface + .globl rm_callback_interface_size +rm_callback_interface_size: + .word _rm_callback_interface_size + +/**************************************************************************** + * END OF REAL-MODE CALLBACK INTERFACE + **************************************************************************** + */ + + +#ifdef PXE_EXPORT +/**************************************************************************** + * PXE CALLBACK INTERFACE + * + * Prepend this to rm_callback_interface to create a real-mode PXE + * callback interface. + **************************************************************************** + */ + .section ".text16", "ax", @progbits + .globl pxe_callback_interface + .code16 +pxe_callback_interface: + +/* Macro to calculate offset of labels within code segment in + * installed copy of code. + */ +#define INSTALLED(x) ( (x) - pxe_callback_interface ) + +/**************************************************************************** + * PXE entry points (!PXE and PXENV+ APIs) + **************************************************************************** + */ + /* in_call mechanism for !PXE API calls */ + .globl _pxe_in_call_far +_pxe_in_call_far: + /* Prepend "PXE API call" and "API version 0x201" to stack */ + pushl $0x201 + jmp 1f + /* in_call mechanism for PXENV+ API calls */ + .globl _pxenv_in_call_far +_pxenv_in_call_far: + /* Prepend "PXE API call" and "API version 0x200" to stack */ + pushl $0x200 +1: pushl $EB_OPCODE_PXE + /* Perform real-mode in_call */ + call pxe_rm_in_call + /* Return */ + addw $8, %sp + lret + +/**************************************************************************** + * PXE installation check (INT 1A) code + **************************************************************************** + */ + .globl _pxe_intercept_int1a +_pxe_intercept_int1a: + pushfw + cmpw $0x5650, %ax + jne 2f +1: /* INT 1A,5650 - Intercept */ + popfw + /* Set up return values according to PXE spec: */ + movw $0x564e, %ax /* AX := 564Eh (VN) */ + pushw %cs:INSTALLED(_pxe_pxenv_segment) + popw %es /* ES:BX := &(PXENV+ structure) */ + movw %cs:INSTALLED(_pxe_pxenv_offset), %bx + clc /* CF is cleared */ + lret $2 /* 'iret' without reloading flags */ +2: /* INT 1A,other - Do not intercept */ + popfw + ljmp %cs:*INSTALLED(_pxe_intercepted_int1a) + + .globl _pxe_intercepted_int1a +_pxe_intercepted_int1a: .word 0,0 + .globl _pxe_pxenv_location +_pxe_pxenv_location: +_pxe_pxenv_offset: .word 0 +_pxe_pxenv_segment: .word 0 + +pxe_rm_in_call: +pxe_attach_rm: + /* rm_callback_interface must be appended here */ + +pxe_callback_interface_end: + + .globl _pxe_callback_interface_size + .equ _pxe_callback_interface_size, pxe_callback_interface_end - pxe_callback_interface + .globl pxe_callback_interface_size +pxe_callback_interface_size: + .word _pxe_callback_interface_size + +#else /* PXE_EXPORT */ + +/* Define symbols used by the linker scripts, to prevent link errors */ + .globl _pxe_callback_interface_size + .equ _pxe_callback_interface_size, 0 + +#endif /* PXE_EXPORT */ + +#else /* CODE16 */ + +/* Define symbols used by the linker scripts, to prevent link errors */ + .globl _rm_callback_interface_size + .equ _rm_callback_interface_size, 0 + .globl _pxe_callback_interface_size + .equ _pxe_callback_interface_size, 0 + +#endif /* CODE16 */ diff --git a/src/arch/i386/core/start16.S b/src/arch/i386/core/start16.S new file mode 100644 index 00000000..72fcfbfb --- /dev/null +++ b/src/arch/i386/core/start16.S @@ -0,0 +1,285 @@ +/***************************************************************************** + * + * THIS FILE IS NOW OBSOLETE. + * + * The functions of this file are now placed in init.S. + * + ***************************************************************************** + */ + +#ifndef PCBIOS +#error "16bit code is only supported with the PCBIOS" +#endif + +#define CODE_SEG 0x08 +#define DATA_SEG 0x10 + +#define EXEC_IN_SITU_MAGIC 0x45524548 /* 'HERE' */ + + .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 + +/***************************************************************************** + * + * start16 : move payload to desired area of memory, set up for exit + * back to prefix, set up for 32-bit code. + * + * Enter (from prefix) with es:di = 0x4552:0x4548 if you want to + * prevent start16 from moving the payload. There are three + * motivations for moving the payload: + * + * 1. It may be in ROM, in which case we need to move it to RAM. + * 2. Whatever loaded us probably didn't know about our memory usage + * beyond the end of the image file. We should claim this memory + * before using it. + * + * Unless the prefix instructs us otherwise we will move the payload to: + * + * An area of memory claimed from the BIOS via 40:13. + * + * We use the main Etherboot stack (within the image target) as our + * stack; we don't rely on the prefix passing us a stack usable for + * anything other than the prefix's return address. The (first 512 + * bytes of the) prefix code segment is copied to a safe archive + * location. + * + * When we return to the prefix (from start32), we copy this code back + * to a new area of memory, restore the prefix's ss:sp and ljmp back + * to the copy of the prefix. The prefix will see a return from + * start16 *but* may be executing at a new location. Code following + * the lcall to start16 must therefore be position-independent and + * must also be within [cs:0000,cs:01ff]. We make absolutely no + * guarantees about the stack contents when the prefix regains + * control. + * + * Trashes just about all registers, including all the segment + * registers. + * + ***************************************************************************** + */ + + .text + .code16 + .arch i386 + .org 0 + .globl _start16 +_start16: + +/***************************************************************************** + * Work out where we are going to place our image (image = optional + * decompressor + runtime). Exit this stage with %ax containing the + * runtime target address divided by 16 (i.e. a real-mode segment + * address). + ***************************************************************************** + */ + movw %es, %ax + cmpw $(EXEC_IN_SITU_MAGIC >> 16), %ax + jne exec_moved + cmpw $(EXEC_IN_SITU_MAGIC & 0xffff), %di + jne exec_moved +exec_in_situ: + /* Prefix has warned us not to move the payload. Simply + * calculate where the image is going to end up, so we can + * work out where to put our stack. + */ + movw %cs, %ax + addw $((payload-_start16)/16), %ax + jmp 99f +exec_moved: + /* Claim an area of base memory from the BIOS and put the + * payload there. arch_relocated_to() will deal with freeing + * up this memory once we've relocated to high memory. + */ + movw $0x40, %ax + movw %ax, %es + movw %es:(0x13), %ax /* FBMS in kb to %ax */ + shlw $6, %ax /* ... in paragraphs */ + subw $__image_size_pgh, %ax /* Subtract space for image */ + shrw $6, %ax /* Round down to nearest kb */ + movw %ax, %es:(0x13) /* ...and claim memory from BIOS */ + shlw $6, %ax +99: + /* At this point %ax contains the segment address for the + * start of the image (image = optional decompressor + runtime). + */ + +/***************************************************************************** + * Set up stack in start32's stack space within the place we're going + * to copy Etherboot to, reserve space for GDT, copy return address + * from prefix stack, store prefix stack address + ***************************************************************************** + */ + popl %esi /* Return address */ + mov %ss, %bx /* %es:di = prefix stack address */ + mov %bx, %es /* (*after* pop of return address) */ + movw %sp, %di + movw $__offset_stack_pgh, %bx /* Set up Etherboot stack */ + addw %ax, %bx + movw %bx, %ss + movw $__stack_size, %sp + subw $(_gdt_end - _gdt), %sp /* Reserve space for GDT */ + movw %sp, %bp /* Record GDT location */ + /* Set up i386_rm_in_call_data_t structure on stack. This is + * the same structure as is set up by rm_in_call. + */ + pushl $0 /* Dummy opcode */ + pushl %esi /* Prefix return address */ + pushfw /* Flags */ + pushw %di /* Prefix %sp */ + pushw %gs /* Segment registers */ + pushw %fs + pushw %es + pushw %ds + pushw %es /* Prefix %ss */ + pushw %cs + /* Stack is now 32-bit aligned */ + + /* %ax still contains image target segment address */ + +/***************************************************************************** + * Calculate image target and prefix code physical addresses, store on stack + * for use in copy routine. + ***************************************************************************** + */ + movzwl %es:-2(%di), %ebx /* Prefix code segment */ + shll $4, %ebx + pushl %ebx /* Prefix code physical address */ + movzwl %ax, %edi /* Image target segment */ + shll $4, %edi + pushl %edi /* Image target physical address */ + +/***************************************************************************** + * Transition to 32-bit protected mode. Set up all segment + * descriptors to use flat physical addresses. + ***************************************************************************** + */ + /* Copy gdt to area reserved on stack + */ + push %cs /* GDT source location -> %ds:%si */ + pop %ds + mov $(_gdt - _start16), %si + push %ss /* GDT target location -> %es:%di */ + pop %es + mov %bp, %di + mov $(_gdt_end - _gdt), %cx + cld + rep movsb /* Copy GDT to stack */ + movl %ss, %eax + shll $4, %eax + movzwl %bp, %ebx + addl %eax, %ebx /* Physical addr of GDT copy -> %ebx */ + movl %ebx, 2(%bp) /* Fill in addr field in GDT */ + + /* Compute the offset I am running at. + */ + movl %cs, %ebx + shll $4, %ebx /* %ebx = offset for start16 symbols */ + + /* Switch to 32bit protected mode. + */ + cli /* Disable interrupts */ + lgdt (%bp) /* Load GDT from stack */ + movl %cr0, %eax /* Set protected mode bit */ + orb $CR0_PE, %al + movl %eax, %cr0 + movl %ss, %eax /* Convert stack pointer to 32bit */ + shll $4, %eax + movzwl %sp, %esp + addl %eax, %esp + movl $DATA_SEG, %eax /* Reload the segment registers */ + movl %eax, %ds + movl %eax, %es + movl %eax, %ss + movl %eax, %fs + movl %eax, %gs + /* Flush prefetch queue, and reload %cs:%eip by effectively ljmping + * to code32_start. Do the jump via pushl and lret because the text + * may not be writable/ + */ + pushl $CODE_SEG + ADDR32 leal (code32_start-_start16)(%ebx), %eax + pushl %eax + DATA32 lret /* DATA32 needed, because we're still in 16-bit mode */ + +_gdt: +gdtarg: + .word _gdt_end - _gdt - 1 /* limit */ + .long 0 /* 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 +_gdt_end: + + .code32 +code32_start: + +/***************************************************************************** + * Copy payload to target location. Do the copy backwards, since if + * there's overlap with a forward copy then it means start16 is going + * to get trashed during the copy anyway... + ***************************************************************************** + */ + popl %edi /* Image target physical address */ + pushl %edi + leal (payload-_start16)(%ebx), %esi /* Image source physical addr */ + movl $__payload_size, %ecx /* Payload size (not image size) */ + addl %ecx, %edi /* Start at last byte (length - 1) */ + decl %edi + addl %ecx, %esi + decl %esi + std /* Backward copy of image */ + rep movsb + cld + popl %edi /* Restore image target physical address */ + leal __decompressor_uncompressed(%edi), %ebx + subl $_text, %ebx /* %ebx = offset for runtime symbols */ + +/***************************************************************************** + * Copy prefix to storage area within Etherboot image. + ***************************************************************************** + */ + popl %esi /* Prefix source physical address */ + pushl %edi + leal _prefix_copy(%ebx), %edi /* Prefix copy phys. addr. */ + leal _eprefix_copy(%ebx), %ecx + subl %edi, %ecx /* Prefix copy size */ + rep movsb /* Forward copy of prefix */ + popl %edi /* Restore image target physical address */ + +/***************************************************************************** + * Record base memory used by Etherboot image + ***************************************************************************** + */ + movl %edi, _prefix_image_basemem (%ebx) + +/***************************************************************************** + * Jump to start of the image (i.e. the decompressor, or start32 if + * non-compressed). + ***************************************************************************** + */ + pushl $0 /* Inform start32 that exit path is 16-bit */ + jmpl *%edi /* Jump to image */ + + .balign 16 + /* Etherboot needs to be 16byte aligned or data that + * is virtually aligned is no longer physically aligned + * which is just nasty in general. 16byte alignment + * should be sufficient though. + */ +payload: diff --git a/src/arch/i386/core/start16.lds b/src/arch/i386/core/start16.lds new file mode 100644 index 00000000..544fc78f --- /dev/null +++ b/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/src/arch/i386/core/start16z.lds b/src/arch/i386/core/start16z.lds new file mode 100644 index 00000000..711bcf7b --- /dev/null +++ b/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/src/arch/i386/core/start32.S b/src/arch/i386/core/start32.S new file mode 100644 index 00000000..6dc3f203 --- /dev/null +++ b/src/arch/i386/core/start32.S @@ -0,0 +1,767 @@ +/* #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 FLAT_CODE_SEG,_pmcs2-_gdt + .equ FLAT_DATA_SEG,_pmds2-_gdt + .equ CR0_PE,1 +#ifdef CONFIG_X86_64 + .equ LM_CODE_SEG, _lmcs-_gdt + .equ LM_DATA_SEG, _lmds-_gdt +#endif + + .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 + +#define BOCHSBP xchgw %bx, %bx + +#include "callbacks.h" +#define NUM_PUSHA_REGS (8) +#define NUM_SEG_REGS (6) + +/* + * 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 + * + * This file is no longer enterered from the top. init.S will jump to + * either _in_call or _rm_in_call, depending on the processor mode + * when init.S was entered. + **************************************************************************/ + .text + .arch i386 + .code32 + +/************************************************************************** +_IN_CALL - make a call in to Etherboot. +**************************************************************************/ + +/* There are two 32-bit entry points: _in_call and _in_call_far, for + * near calls and far calls respectively. Both should be called with + * flat physical addresses. They will result in a call to the C + * routine in_call(); see there for API details. + * + * Note that this routine makes fairly heavy use of the stack and no + * use of fixed data areas. This is because it must be re-entrant; + * there may be more than one concurrent call in to Etherboot. + */ + +#define IC_OFFSET_VA_LIST_PTR ( 0 ) +#define IC_OFFSET_VA_LIST_PTR_E ( IC_OFFSET_VA_LIST_PTR + 4 ) +#define IC_OFFSET_REGISTERS ( IC_OFFSET_VA_LIST_PTR_E ) +#define IC_OFFSET_REGISTERS_E ( IC_OFFSET_REGISTERS + ( NUM_PUSHA_REGS * 4 ) ) +#define IC_OFFSET_SEG_REGS ( IC_OFFSET_REGISTERS_E ) +#define IC_OFFSET_SEG_REGS_E ( IC_OFFSET_SEG_REGS + ( NUM_SEG_REGS * 2 ) ) +#define IC_OFFSET_GDT ( IC_OFFSET_SEG_REGS_E ) +#define IC_OFFSET_GDT_E ( IC_OFFSET_GDT + 8 ) +#define IC_OFFSET_FLAGS ( IC_OFFSET_GDT_E ) +#define IC_OFFSET_FLAGS_E ( IC_OFFSET_FLAGS + 4 ) +#define IC_OFFSET_RETADDR ( IC_OFFSET_FLAGS_E ) +#define IC_OFFSET_RETADDR_E ( IC_OFFSET_RETADDR + 8 ) +#define IC_OFFSET_ORIG_STACK ( IC_OFFSET_RETADDR ) +#define IC_OFFSET_OPCODE ( IC_OFFSET_ORIG_STACK + 8 ) +#define IC_OFFSET_OPCODE_E ( IC_OFFSET_OPCODE + 4 ) +#define IC_OFFSET_VA_LIST ( IC_OFFSET_OPCODE_E ) + + .code32 + .globl _in_call + .globl _in_call_far +_in_call: + /* Expand to far return address */ + pushl %eax /* Store %eax */ + xorl %eax, %eax + movw %cs, %ax + xchgl %eax, 4(%esp) /* 4(%esp) = %cs, %eax = ret addr */ + xchgl %eax, 0(%esp) /* 0(%esp) = ret addr, restore %eax */ +_in_call_far: + /* Store flags */ + pushfl + /* Store the GDT */ + subl $8, %esp + sgdt 0(%esp) + /* Store segment register values */ + pushw %gs + pushw %fs + pushw %es + pushw %ds + pushw %ss + pushw %cs + /* Store general-purpose register values */ + pushal + /* Replace %esp in store with physical %esp value on entry */ + leal (IC_OFFSET_ORIG_STACK - IC_OFFSET_REGISTERS)(%esp), %eax + movl %eax, (IC_OFFSET_REGISTERS - IC_OFFSET_REGISTERS + 12)(%esp) + /* Store va_list pointer (physical address) */ + leal (IC_OFFSET_VA_LIST - IC_OFFSET_VA_LIST_PTR_E)(%esp), %eax + pushl %eax + /* IC_OFFSET_*(%esp) are now valid */ + + /* Switch to virtual addresses */ + call _phys_to_virt + + /* Fixup the va_list pointer */ + movl virt_offset, %ebp + subl %ebp, IC_OFFSET_VA_LIST_PTR(%esp) + + /* Check opcode for EB_USE_INTERNAL_STACK flag */ + movl IC_OFFSET_OPCODE(%esp), %eax + testl $EB_USE_INTERNAL_STACK, %eax + je 2f + /* Use internal stack flag set */ + /* Check %esp is not already in internal stack range */ + leal _stack, %esi /* %esi = bottom of internal stack */ + leal _estack, %edi /* %edi = top of internal stack */ + cmpl %esi, %esp + jb 1f + cmpl %edi, %esp + jbe 2f +1: /* %esp not currently in internal stack range */ + movl %esp, %esi /* %esi = original stack */ + movl $IC_OFFSET_OPCODE_E, %ecx /* %ecx = length to transfer */ + subl %ecx, %edi /* %edi = internal stack pos */ + movl %edi, %esp /* = new %esp */ + rep movsb /* Copy data to internal stack */ +2: + + /* Call to C code */ + call i386_in_call + + /* Set %eax (return code from C) in registers structure on + * stack, so that we return it to the caller. + */ + movl %eax, (IC_OFFSET_REGISTERS + 28)(%esp) + + /* Calculate physical continuation address */ + movl virt_offset, %ebp + movzwl (IC_OFFSET_SEG_REGS + 0)(%esp), %eax /* %cs */ + movzwl (IC_OFFSET_SEG_REGS + 2)(%esp), %ebx /* %ss */ + pushl %eax /* Continuation segment */ + leal 1f(%ebp), %eax + pushl %eax /* Continuation offset */ + + /* Restore caller's GDT */ + cli /* Temporarily disable interrupts */ + lgdt (8+IC_OFFSET_GDT)(%esp) + /* Reset %ss and adjust %esp */ + movw %bx, %ss + addl %ebp, %esp + lret /* Reload %cs:eip, flush prefetch */ +1: + + /* Skip va_list ptr */ + popl %eax + /* Reload general-purpose registers to be returned */ + popal + /* Reload segment registers as passed in from caller */ + popw %gs + popw %fs + popw %es + popw %ds + addl $(4+8), %esp /* Skip %cs, %ss and GDT (already reloaded) */ + /* Restore flags (including revert of interrupt status) */ + popfl + + /* Restore physical %esp from entry. It will only be + * different if EB_USE_INTERNAL_STACK was specified. + */ + movl ( 12 + IC_OFFSET_REGISTERS - IC_OFFSET_RETADDR )(%esp), %esp + + /* Check for EB_SKIP_OPCODE */ + pushfl + testl $EB_SKIP_OPCODE, 12(%esp) + jnz 1f + /* Normal return */ + popfl + lret +1: /* Return and skip opcode */ + popfl + lret $4 + +/************************************************************************** +RELOCATE_TO - relocate etherboot to the specified address +**************************************************************************/ + .globl relocate_to +relocate_to: + /* Save the callee save registers */ + pushl %ebp + pushl %esi + pushl %edi + + /* Compute the virtual destination address */ + movl 16(%esp), %edi # dest + subl virt_offset, %edi + + + /* Compute the new value of virt_offset */ + movl 16(%esp), %ebp # virt_offset + subl $_text, %ebp + + /* Fixup the gdt */ + pushl $_pmcs + pushl %ebp # virt_offset + call set_seg_base + addl $8, %esp + + /* Fixup gdtarg */ + leal _gdt(%ebp), %eax + movl %eax, gdtarg +2 + + /* Fixup virt_offset */ + movl %ebp, virt_offset + + /* Load the move parameters */ + movl $_text, %esi + movl $_end, %ecx + subl %esi, %ecx + + /* Move etherboot uses %esi, %edi, %ecx */ + rep + movsb + + /* Reload the gdt */ + cs + lgdt gdtarg + + /* Reload %cs */ + 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 %eax,%fs + movl %eax,%gs + + /* Restore the callee save registers */ + popl %edi + popl %esi + popl %ebp + + /* return */ + ret + +/************************************************************************** +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 $FLAT_CODE_SEG + 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 */ + movl 52(%esp), %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 */ + +/************************************************************************** +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 + +/************************************************************************** +_VIRT_TO_PHYS - Transition from virtual to physical addresses + Preserves all preservable registers and flags +**************************************************************************/ + .globl _virt_to_phys +_virt_to_phys: + pushfl + pushl %ebp + pushl %eax + movl virt_offset, %ebp /* Load virt_offset */ + addl %ebp, 12(%esp) /* Adjust the return address */ + + /* reload the code segment */ + pushl $FLAT_CODE_SEG + leal 1f(%ebp), %eax + pushl %eax + lret + +1: + /* reload other segment registers */ + movl $FLAT_DATA_SEG, %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %ss + addl %ebp, %esp /* Adjust the stack pointer */ + movl %eax, %fs + movl %eax, %gs + + popl %eax + popl %ebp + popfl + ret + + +/************************************************************************** +_PHYS_TO_VIRT - Transition from using physical to virtual addresses + Preserves all preservable registers and flags +**************************************************************************/ + .globl _phys_to_virt +_phys_to_virt: + pushfl + pushl %ebp + pushl %eax + + call 1f +1: popl %ebp + subl $1b, %ebp + movl %ebp, virt_offset(%ebp) + + /* Fixup the gdt */ + leal _pmcs(%ebp), %eax + pushl %eax + pushl %ebp + call set_seg_base + addl $8, %esp + + /* Fixup gdtarg */ + leal _gdt(%ebp), %eax + movl %eax, (gdtarg+2)(%ebp) + + /* Load the global descriptor table */ + cli + lgdt %cs:gdtarg(%ebp) + ljmp $KERN_CODE_SEG, $1f +1: + /* reload other segment regsters */ + movl $KERN_DATA_SEG, %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %ss + subl %ebp, %esp /* Adjust the stack pointer */ + movl %eax, %fs + movl %eax, %gs + + subl %ebp, 12(%esp) /* Adjust the return address */ + popl %eax + popl %ebp + popfl + ret + + +/************************************************************************** +SET_SEG_BASE - Set the base address of a segment register +**************************************************************************/ + .globl set_seg_base +set_seg_base: + pushl %eax + pushl %ebx + movl 12(%esp), %eax /* %eax = base address */ + movl 16(%esp), %ebx /* %ebx = &code_descriptor */ + movw %ax, (0+2)(%ebx) /* CS base bits 0-15 */ + movw %ax, (8+2)(%ebx) /* DS base bits 0-15 */ + shrl $16, %eax + movb %al, (0+4)(%ebx) /* CS base bits 16-23 */ + movb %al, (8+4)(%ebx) /* DS base bits 16-23 */ + movb %ah, (0+7)(%ebx) /* CS base bits 24-31 */ + movb %ah, (8+7)(%ebx) /* DS base bits 24-31 */ + popl %ebx + popl %eax + ret + +/************************************************************************** +GLOBAL DESCRIPTOR TABLE +**************************************************************************/ + .data + .align 4 + + .globl _gdt + .globl gdtarg +_gdt: +gdtarg: + .word _gdt_end - _gdt - 1 /* limit */ + .long _gdt /* addr */ + .word 0 + + .globl _pmcs +_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,(0&0xffff) + .byte (0>>16),0x9b,0x00,(0>>24) + +_rmds: + /* 16 bit real mode data segment */ + .word 0xffff,(0&0xffff) + .byte (0>>16),0x93,0x00,(0>>24) + +_pmcs2: + /* 32 bit protected mode code segment, base 0 */ + .word 0xffff,0 + .byte 0,0x9f,0xcf,0 + +_pmds2: + /* 32 bit protected mode data segment, base 0 */ + .word 0xffff,0 + .byte 0,0x93,0xcf,0 + +#ifdef CONFIG_X86_64 +_lmcs: + /* 64bit long mode code segment, base 0 */ + .word 0xffff, 0 + .byte 0x00, 0x9f, 0xaf , 0x00 +_lmds: + /* 64bit long mode data segment, base 0 */ + .word 0xffff, 0 + .byte 0x00, 0x93, 0xcf, 0x00 +#endif +_gdt_end: + + /* The initial register contents */ + .balign 4 + .globl initial_regs +initial_regs: + .fill 8, 4, 0 + + /* The virtual address offset */ + .globl virt_offset +virt_offset: + .long 0 + + .section ".stack" + .p2align 3 + /* allocate a 4K stack in the stack segment */ + .globl _stack +_stack: + .space 4096 + .globl _estack +_estack: +#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/src/arch/i386/core/tagged_loader.c b/src/arch/i386/core/tagged_loader.c new file mode 100644 index 00000000..9c6d6c25 --- /dev/null +++ b/src/arch/i386/core/tagged_loader.c @@ -0,0 +1,201 @@ +#include "realmode.h" +#include "segoff.h" + +struct segheader +{ + unsigned char length; + unsigned char vendortag; + unsigned char reserved; + unsigned char flags; + unsigned long loadaddr; + unsigned long imglength; + unsigned long memlength; +}; + +struct imgheader +{ + unsigned long magic; + unsigned long length; /* and flags */ + union + { + segoff_t segoff; + unsigned long location; + } u; + unsigned long execaddr; +}; + +/* Keep all context about loaded image in one place */ +static struct tagged_context +{ + struct imgheader img; /* copy of header */ + unsigned long linlocation; /* addr of header */ + unsigned long last0, last1; /* of prev segment */ + unsigned long segaddr, seglen; /* of current segment */ + unsigned char segflags; + unsigned char first; + unsigned long curaddr; +} tctx; + +#define TAGGED_PROGRAM_RETURNS (tctx.img.length & 0x00000100) /* bit 8 */ +#define LINEAR_EXEC_ADDR (tctx.img.length & 0x80000000) /* bit 31 */ + +static sector_t tagged_download(unsigned char *data, unsigned int len, int eof); +void xstart16 (unsigned long execaddr, segoff_t location, + void *bootp); + +static inline os_download_t tagged_probe(unsigned char *data, unsigned int len) +{ + struct segheader *sh; + unsigned long loc; + if (*((uint32_t *)data) != 0x1B031336L) { + return 0; + } + printf("(NBI)"); + /* If we don't have enough data give up */ + if (len < 512) + return dead_download; + /* Zero all context info */ + memset(&tctx, 0, sizeof(tctx)); + /* Copy first 4 longwords */ + memcpy(&tctx.img, data, sizeof(tctx.img)); + /* Memory location where we are supposed to save it */ + tctx.segaddr = tctx.linlocation = + ((tctx.img.u.segoff.segment) << 4) + tctx.img.u.segoff.offset; + if (!prep_segment(tctx.segaddr, tctx.segaddr + 512, tctx.segaddr + 512, + 0, 512)) { + return dead_download; + } + /* Now verify the segments we are about to load */ + loc = 512; + for(sh = (struct segheader *)(data + + ((tctx.img.length & 0x0F) << 2) + + ((tctx.img.length & 0xF0) >> 2) ); + (sh->length > 0) && ((unsigned char *)sh < data + 512); + sh = (struct segheader *)((unsigned char *)sh + + ((sh->length & 0x0f) << 2) + ((sh->length & 0xf0) >> 2)) ) { + if (!prep_segment( + sh->loadaddr, + sh->loadaddr + sh->imglength, + sh->loadaddr + sh->imglength, + loc, loc + sh->imglength)) { + return dead_download; + } + loc = loc + sh->imglength; + if (sh->flags & 0x04) + break; + } + if (!(sh->flags & 0x04)) + return dead_download; + /* Grab a copy */ + memcpy(phys_to_virt(tctx.segaddr), data, 512); + /* Advance to first segment descriptor */ + tctx.segaddr += ((tctx.img.length & 0x0F) << 2) + + ((tctx.img.length & 0xF0) >> 2); + /* Remember to skip the first 512 data bytes */ + tctx.first = 1; + + return tagged_download; + +} +static sector_t tagged_download(unsigned char *data, unsigned int len, int eof) +{ + int i; + + if (tctx.first) { + tctx.first = 0; + if (len > 512) { + len -= 512; + data += 512; + /* and fall through to deal with rest of block */ + } else + return 0; + } + for (;;) { + if (len == 0) /* Detect truncated files */ + eof = 0; + while (tctx.seglen == 0) { + struct segheader sh; + if (tctx.segflags & 0x04) { + done(1); + if (LINEAR_EXEC_ADDR) { + int result; + /* no gateA20_unset for PM call */ + result = xstart32(tctx.img.execaddr, + virt_to_phys(&loaderinfo), + tctx.linlocation, + virt_to_phys(BOOTP_DATA_ADDR)); + printf("Secondary program returned %d\n", + result); + if (!TAGGED_PROGRAM_RETURNS) { + /* We shouldn't have returned */ + result = -2; + } + if (result == 0) + result = -2; + longjmp(restart_etherboot, result); + + } else { + gateA20_unset(); + xstart16(tctx.img.execaddr, + tctx.img.u.segoff, + BOOTP_DATA_ADDR); + longjmp(restart_etherboot, -2); + } + } + sh = *((struct segheader *)phys_to_virt(tctx.segaddr)); + tctx.seglen = sh.imglength; + if ((tctx.segflags = sh.flags & 0x03) == 0) + tctx.curaddr = sh.loadaddr; + else if (tctx.segflags == 0x01) + tctx.curaddr = tctx.last1 + sh.loadaddr; + else if (tctx.segflags == 0x02) + tctx.curaddr = (Address)(meminfo.memsize * 1024L + + 0x100000L) + - sh.loadaddr; + else + tctx.curaddr = tctx.last0 - sh.loadaddr; + tctx.last1 = (tctx.last0 = tctx.curaddr) + sh.memlength; + tctx.segflags = sh.flags; + tctx.segaddr += ((sh.length & 0x0F) << 2) + + ((sh.length & 0xF0) >> 2); + /* Avoid lock-up */ + if ( sh.length == 0 ) longjmp(restart_etherboot, -2); + } + if ((len <= 0) && !eof) + break; + i = (tctx.seglen > len) ? len : tctx.seglen; + memcpy(phys_to_virt(tctx.curaddr), data, i); + tctx.seglen -= i; + tctx.curaddr += i; + len -= i; + data += i; + } + return 0; +} + +void xstart16 (unsigned long execaddr, segoff_t location, + void *bootp) { + struct { + segoff_t execaddr; + segoff_t location; + segoff_t bootp; + } PACKED in_stack; + + /* AFAICT, execaddr is actually already a segment:offset */ + *((unsigned long *)&in_stack.execaddr) = execaddr; + in_stack.location = location; + in_stack.bootp.segment = SEGMENT(bootp); + in_stack.bootp.offset = OFFSET(bootp); + + RM_FRAGMENT(rm_xstart16, + "popl %eax\n\t" /* Calculated lcall */ + "pushw %cs\n\t" + "call 1f\n1:\tpopw %bp\n\t" + "leaw (2f-1b)(%bp), %bx\n\t" + "pushw %bx\n\t" + "pushl %eax\n\t" + "lret\n2:\n\t" + ); + + real_call ( rm_xstart16, &in_stack, NULL ); +} diff --git a/src/arch/i386/core/video_subr.c b/src/arch/i386/core/video_subr.c new file mode 100644 index 00000000..dccdd97c --- /dev/null +++ b/src/arch/i386/core/video_subr.c @@ -0,0 +1,94 @@ +/* + * + * modified from linuxbios code + * by Cai Qiang + * + */ + +#ifdef CONSOLE_DIRECT_VGA + +#include +#include + +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) +{ + int i; + u16 *ss = (u16 *) s; + + for (i = 0; i < n; i++) { + ss[i] = ( u16 ) c; + } +} + +void video_init(void) +{ + static int inited=0; + + vidmem = (unsigned 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] = ' '; +} + +void vga_putc(unsigned char 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); +} + +#endif + diff --git a/src/arch/i386/core/wince_loader.c b/src/arch/i386/core/wince_loader.c new file mode 100644 index 00000000..f452b659 --- /dev/null +++ b/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/src/arch/i386/drivers/net/undi.c b/src/arch/i386/drivers/net/undi.c new file mode 100644 index 00000000..084fc18a --- /dev/null +++ b/src/arch/i386/drivers/net/undi.c @@ -0,0 +1,1458 @@ +/************************************************************************** +Etherboot - BOOTP/TFTP Bootstrap Program +UNDI NIC driver for Etherboot + +This file Copyright (C) 2003 Michael Brown +of Fen Systems Ltd. (http://www.fensystems.co.uk/). All rights +reserved. + +$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. + */ + +#ifdef PCBIOS + +/* 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 "pci.h" +/* UNDI and PXE defines. Includes pxe.h. */ +#include "undi.h" +/* 8259 PIC defines */ +#include "pic8259.h" +/* Real-mode calls */ +#include "realmode.h" +/* E820 map mangler */ +#include "hidemem.h" + +/* NIC specific static variables go here */ +static undi_t undi = { + .pnp_bios = NULL, + .rom = NULL, + .undi_rom_id = NULL, + .pxe = NULL, + .pxs = NULL, + .xmit_data = NULL, + .base_mem_data = NULL, + .driver_code = NULL, + .driver_code_size = 0, + .driver_data = NULL, + .driver_data_size = 0, + .xmit_buffer = NULL, + .prestarted = 0, + .started = 0, + .initialized = 0, + .opened = 0, + .pci = { 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, NULL }, + .irq = IRQ_NONE +}; + +/* Function prototypes */ +static int allocate_base_mem_data ( void ); +static int free_base_mem_data ( void ); +static int eb_pxenv_undi_shutdown ( void ); +static int eb_pxenv_stop_undi ( void ); +static int undi_unload_base_code ( void ); +static int undi_full_shutdown ( void ); + +/* Trivial/nontrivial IRQ handler selection */ +#ifdef UNDI_NONTRIVIAL_IRQ +static void nontrivial_irq_handler ( void ); +static void nontrivial_irq_handler_end ( void ); +static int install_nontrivial_irq_handler ( irq_t irq ); +static int remove_nontrivial_irq_handler ( irq_t irq ); +static int nontrivial_irq_triggered ( irq_t irq ); +static int copy_nontrivial_irq_handler ( void *target, size_t target_size ); +#define NONTRIVIAL_IRQ_HANDLER_SIZE FRAGMENT_SIZE(nontrivial_irq_handler) +#define install_undi_irq_handler(irq) install_nontrivial_irq_handler(irq) +#define remove_undi_irq_handler(irq) remove_nontrivial_irq_handler(irq) +#define undi_irq_triggered(irq) nontrivial_irq_triggered(irq) +#define UNDI_IRQ_HANDLER_SIZE NONTRIVIAL_IRQ_HANDLER_SIZE +#define copy_undi_irq_handler(dest,size) copy_nontrivial_irq_handler(dest,size) +#else +#define install_undi_irq_handler(irq) install_trivial_irq_handler(irq) +#define remove_undi_irq_handler(irq) remove_trivial_irq_handler(irq) +#define undi_irq_triggered(irq) trivial_irq_triggered(irq) +#define UNDI_IRQ_HANDLER_SIZE TRIVIAL_IRQ_HANDLER_SIZE +#define copy_undi_irq_handler(dest,size) copy_trivial_irq_handler(dest,size) +#endif /* UNDI_NONTRIVIAL_IRQ */ + +/* Size of variable-length data in base_mem_data */ +#define BASE_MEM_VARDATA_SIZE ( UNDI_IRQ_HANDLER_SIZE > e820mangler_size ? \ + UNDI_IRQ_HANDLER_SIZE : e820mangler_size ) + +/************************************************************************** + * Utility functions + **************************************************************************/ + +/* Checksum a block. + */ + +static uint8_t checksum ( void *block, size_t size ) { + uint8_t sum = 0; + uint16_t i = 0; + for ( i = 0; i < size; i++ ) { + sum += ( ( uint8_t * ) block )[i]; + } + return sum; +} + +/* Print the status of a !PXE structure + */ + +static void pxe_dump ( void ) { + printf ( "API %hx:%hx St %hx:%hx UD %hx:%hx UC %hx:%hx " + "BD %hx:%hx BC %hx:%hx\n", + undi.pxe->EntryPointSP.segment, undi.pxe->EntryPointSP.offset, + undi.pxe->Stack.Seg_Addr, undi.pxe->Stack.Seg_Size, + undi.pxe->UNDIData.Seg_Addr, undi.pxe->UNDIData.Seg_Size, + undi.pxe->UNDICode.Seg_Addr, undi.pxe->UNDICode.Seg_Size, + undi.pxe->BC_Data.Seg_Addr, undi.pxe->BC_Data.Seg_Size, + undi.pxe->BC_Code.Seg_Addr, undi.pxe->BC_Code.Seg_Size ); +} + +/* Allocate/free space for structures that must reside in base memory + */ + +static int allocate_base_mem_data ( void ) { + /* Allocate space in base memory. + * Initialise pointers to base memory structures. + */ + if ( undi.base_mem_data == NULL ) { + undi.base_mem_data = + allot_base_memory ( sizeof(undi_base_mem_data_t) + + BASE_MEM_VARDATA_SIZE ); + if ( undi.base_mem_data == NULL ) { + printf ( "Failed to allocate base memory\n" ); + free_base_mem_data(); + return 0; + } + memset ( undi.base_mem_data, 0, sizeof(undi_base_mem_data_t) ); + undi.pxs = &undi.base_mem_data->pxs; + undi.xmit_data = &undi.base_mem_data->xmit_data; + undi.xmit_buffer = undi.base_mem_data->xmit_buffer; + } + return 1; +} + +static int free_base_mem_data ( void ) { + if ( undi.base_mem_data != NULL ) { + forget_base_memory ( undi.base_mem_data, + sizeof(undi_base_mem_data_t) + + BASE_MEM_VARDATA_SIZE ); + undi.base_mem_data = NULL; + undi.pxs = NULL; + undi.xmit_data = NULL; + undi.xmit_buffer = NULL; + copy_undi_irq_handler ( NULL, 0 ); + } + return 1; +} + +static void assemble_firing_squad ( firing_squad_lineup_t *lineup, + void *start, size_t size, + firing_squad_shoot_t shoot ) { + int target; + int index; + int bit; + int start_kb = virt_to_phys(start) >> 10; + int end_kb = ( virt_to_phys(start+size) + (1<<10) - 1 ) >> 10; + + for ( target = start_kb; target <= end_kb; target++ ) { + index = FIRING_SQUAD_TARGET_INDEX ( target ); + bit = FIRING_SQUAD_TARGET_BIT ( target ); + lineup->targets[index] = ( shoot << bit ) | + ( lineup->targets[index] & ~( 1 << bit ) ); + } +} + +static void shoot_targets ( firing_squad_lineup_t *lineup ) { + int shoot_this_target = 0; + int shoot_last_target = 0; + int start_target = 0; + int target; + + for ( target = 0; target <= 640; target++ ) { + shoot_this_target = ( target == 640 ? 0 : + ( 1 << FIRING_SQUAD_TARGET_BIT(target) ) & + lineup->targets[FIRING_SQUAD_TARGET_INDEX(target)] ); + if ( shoot_this_target && !shoot_last_target ) { + start_target = target; + } else if ( shoot_last_target && !shoot_this_target ) { + size_t range_size = ( target - start_target ) << 10; + forget_base_memory ( phys_to_virt( start_target<<10 ), + range_size ); + } + shoot_last_target = shoot_this_target; + } +} + +/* Debug macros + */ + +#ifdef TRACE_UNDI +#define DBG(...) printf ( __VA_ARGS__ ) +#else +#define DBG(...) +#endif + +#define UNDI_STATUS(pxs) ( (pxs)->Status == PXENV_EXIT_SUCCESS ? \ + "SUCCESS" : \ + ( (pxs)->Status == PXENV_EXIT_FAILURE ? \ + "FAILURE" : "UNKNOWN" ) ) + +/************************************************************************** + * Base memory scanning functions + **************************************************************************/ + +/* Locate the $PnP structure indicating a PnP BIOS. + */ + +static int hunt_pnp_bios ( void ) { + uint32_t off = 0x10000; + + printf ( "Hunting for PnP BIOS..." ); + while ( off > 0 ) { + off -= 16; + undi.pnp_bios = (pnp_bios_t *) phys_to_virt ( 0xf0000 + off ); + if ( undi.pnp_bios->signature == PNP_BIOS_SIGNATURE ) { + printf ( "found $PnP at f000:%hx...", off ); + if ( checksum(undi.pnp_bios,sizeof(pnp_bios_t)) !=0) { + printf ( "invalid checksum\n..." ); + continue; + } + printf ( "ok\n" ); + return 1; + } + } + printf ( "none found\n" ); + undi.pnp_bios = NULL; + return 0; +} + +/* Locate the !PXE structure indicating a loaded UNDI driver. + */ + +static int hunt_pixie ( void ) { + static uint32_t ptr = 0; + pxe_t *pxe = NULL; + + printf ( "Hunting for pixies..." ); + if ( ptr == 0 ) ptr = 0xa0000; + while ( ptr > 0x10000 ) { + ptr -= 16; + pxe = (pxe_t *) phys_to_virt ( ptr ); + if ( memcmp ( pxe->Signature, "!PXE", 4 ) == 0 ) { + printf ( "found !PXE at %x...", ptr ); + if ( checksum ( pxe, sizeof(pxe_t) ) != 0 ) { + printf ( "invalid checksum\n..." ); + continue; + } + if ( ptr < get_free_base_memory() ) { + printf ( "in free base memory!\n\n" + "WARNING: a valid !PXE structure was " + "found in an area of memory marked " + "as free!\n\n" ); + undi.pxe = pxe; + pxe_dump(); + undi.pxe = NULL; + printf ( "\nIgnoring and continuing, but this " + "may cause problems later!\n\n" ); + continue; + } + printf ( "ok\n" ); + undi.pxe = pxe; + pxe_dump(); + printf ( "Resetting pixie...\n" ); + undi_unload_base_code(); + eb_pxenv_stop_undi(); + pxe_dump(); + return 1; + } + } + printf ( "none found\n" ); + ptr = 0; + return 0; +} + +/* Locate PCI PnP ROMs. + */ + +static int hunt_rom ( void ) { + static uint32_t ptr = 0; + + /* If we are not a PCI device, we cannot search for a ROM that + * matches us (?) + */ + if ( ! undi.pci.vendor ) + return 0; + + printf ( "Hunting for ROMs..." ); + if ( ptr == 0 ) ptr = 0x100000; + while ( ptr > 0x0c0000 ) { + ptr -= 0x800; + undi.rom = ( rom_t * ) phys_to_virt ( ptr ); + if ( undi.rom->signature == ROM_SIGNATURE ) { + pcir_header_t *pcir_header = NULL; + pnp_header_t *pnp_header = NULL; + + printf ( "found 55AA at %x...", ptr ); + if ( undi.rom->pcir_off == 0 ) { + printf ( "not a PCI ROM\n..." ); + continue; + } + pcir_header = (pcir_header_t*)( ( void * ) undi.rom + + undi.rom->pcir_off ); + if ( pcir_header->signature != PCIR_SIGNATURE ) { + printf ( "invalid PCI signature\n..." ); + continue; + } + printf ( "PCI:%hx:%hx...", pcir_header->vendor_id, + pcir_header->device_id ); + if ( ( pcir_header->vendor_id != undi.pci.vendor ) || + ( pcir_header->device_id != undi.pci.dev_id ) ) { + printf ( "not me (%hx:%hx)\n...", + undi.pci.vendor, + undi.pci.dev_id ); + continue; + } + if ( undi.rom->pnp_off == 0 ) { + printf ( "not a PnP ROM\n..." ); + continue; + } + pnp_header = (pnp_header_t*)( ( void * ) undi.rom + + undi.rom->pnp_off ); + if ( pnp_header->signature != PNP_SIGNATURE ) { + printf ( "invalid $PnP signature\n..." ); + continue; + } + if ( checksum(pnp_header,sizeof(pnp_header_t)) != 0 ) { + printf ( "invalid PnP checksum\n..." ); + continue; + } + printf ( "ok\nROM contains %s by %s\n", + pnp_header->product_str_off==0 ? "(unknown)" : + (void*)undi.rom+pnp_header->product_str_off, + pnp_header->manuf_str_off==0 ? "(unknown)" : + (void*)undi.rom+pnp_header->manuf_str_off ); + return 1; + } + } + printf ( "none found\n" ); + ptr = 0; + undi.rom = NULL; + return 0; +} + +/* Locate ROMs containing UNDI drivers. + */ + +static int hunt_undi_rom ( void ) { + while ( hunt_rom() ) { + if ( undi.rom->undi_rom_id_off == 0 ) { + printf ( "Not a PXE ROM\n" ); + continue; + } + undi.undi_rom_id = (undi_rom_id_t *) + ( (void *)undi.rom + undi.rom->undi_rom_id_off ); + if ( undi.undi_rom_id->signature != UNDI_SIGNATURE ) { + printf ( "Invalid UNDI signature\n" ); + continue; + } + if ( checksum ( undi.undi_rom_id, + undi.undi_rom_id->struct_length ) != 0 ) { + printf ( "Invalid checksum\n" ); + continue; + } + printf ( "Located UNDI ROM supporting revision %d.%d.%d\n", + undi.undi_rom_id->undi_rev[2], + undi.undi_rom_id->undi_rev[1], + undi.undi_rom_id->undi_rev[0] ); + return 1; + } + return 0; +} + +/************************************************************************** + * Low-level UNDI API call wrappers + **************************************************************************/ + +/* Make a real-mode UNDI API call to the UNDI routine at + * routine_seg:routine_off, passing in three uint16 parameters on the + * real-mode stack. + */ + +static PXENV_EXIT_t _undi_call ( uint16_t routine_seg, + uint16_t routine_off, uint16_t st0, + uint16_t st1, uint16_t st2 ) { + PXENV_EXIT_t ret = PXENV_EXIT_FAILURE; + struct { + segoff_t routine; + uint16_t st0; + uint16_t st1; + uint16_t st2; + } PACKED in_stack = { + { routine_off, routine_seg }, st0, st1, st2 + }; + + RM_FRAGMENT(rm_undi_call, + "popw %di\n\t" /* %es:di = routine */ + "popw %es\n\t" + "pushw %cs\n\t" /* set up return address */ + "call 1f\n\t1:popw %bx\n\t" + "leaw (2f-1b)(%bx), %ax\n\t" + "pushw %ax\n\t" + "pushw %es\n\t" /* routine address to stack */ + "pushw %di\n\t" + "lret\n\t" /* calculated lcall */ + "\n2:\n\t" /* continuation point */ + ); + + /* Parameters are left on stack: set out_stack = in_stack */ + ret = real_call ( rm_undi_call, &in_stack, &in_stack ); + + /* 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(); + + return ret; +} + +/* Make a real-mode call to the UNDI loader routine at + * routine_seg:routine_off, passing in the seg:off address of a + * pxenv_structure on the real-mode stack. + */ + +static int undi_call_loader ( void ) { + PXENV_EXIT_t pxenv_exit = PXENV_EXIT_FAILURE; + + /* Hide Etherboot around the loader, so that the PXE stack + * doesn't trash our memory areas + */ + install_e820mangler ( undi.base_mem_data->e820mangler ); + hide_etherboot(); + pxenv_exit = _undi_call ( SEGMENT( undi.rom ), + undi.undi_rom_id->undi_loader_off, + OFFSET( undi.pxs ), + SEGMENT( undi.pxs ), + 0 /* Unused for UNDI loader API */ ); + if ( !unhide_etherboot() ) { + printf ( "FATAL: corrupt INT15\n" ); + return 0; + } + + /* Return 1 for success, to be consistent with other routines */ + if ( pxenv_exit == PXENV_EXIT_SUCCESS ) return 1; + printf ( "UNDI loader call failed with status %#hx\n", + undi.pxs->Status ); + return 0; +} + +/* Make a real-mode UNDI API call, passing in the opcode and the + * seg:off address of a pxenv_structure on the real-mode stack. + * + * Two versions: undi_call() will automatically report any failure + * codes, undi_call_silent() will not. + */ + +static int undi_call_silent ( uint16_t opcode ) { + PXENV_EXIT_t pxenv_exit = PXENV_EXIT_FAILURE; + + pxenv_exit = _undi_call ( undi.pxe->EntryPointSP.segment, + undi.pxe->EntryPointSP.offset, + opcode, + OFFSET( undi.pxs ), + SEGMENT( undi.pxs ) ); + /* Return 1 for success, to be consistent with other routines */ + return pxenv_exit == PXENV_EXIT_SUCCESS ? 1 : 0; +} + +static int undi_call ( uint16_t opcode ) { + if ( undi_call_silent ( opcode ) ) return 1; + printf ( "UNDI API call %#hx failed with status %#hx\n", + opcode, undi.pxs->Status ); + return 0; +} + +#ifdef UNDI_NONTRIVIAL_IRQ +/* IRQ handler that actually calls PXENV_UNDI_ISR. It's probably + * better to use the trivial IRQ handler, since this seems to work for + * just about all known NICs and doesn't involve making a PXE API call + * in interrupt context. + * + * This routine is mainly used for testing the Etherboot PXE stack's + * ability to be called in interrupt context. It is not compiled in + * by default. + * + * This code has fewer safety checks than those in the + * trivial_irq_handler routines. These are omitted because this code + * is not intended for mainstream use. + */ + +uint16_t nontrivial_irq_previous_trigger_count = 0; + +static int copy_nontrivial_irq_handler ( void *target, + size_t target_size __unused ) { + RM_FRAGMENT(nontrivial_irq_handler, + /* Will be installed on a paragraph boundary, so access variables + * using %cs:(xxx-irqstart) + */ + "\n\t" + "irqstart:\n\t" + /* Fields here must match those in undi_irq_handler_t */ + "chain_to:\t.word 0,0\n\t" + "irq_chain:\t.byte 0,0,0,0\n\t" + "entry:\t.word 0,0\n\t" + "count_all:\t.word 0\n\t" + "count_ours:\t.word 0\n\t" + "undi_isr:\n\t" + "undi_isr_Status:\t.word 0\n\t" + "undi_isr_FuncFlag:\t.word 0\n\t" + "undi_isr_others:\t.word 0,0,0,0,0,0\n\t" + "handler:\n\t" + /* Assume that PXE stack will corrupt everything */ + "pushal\n\t" + "push %ds\n\t" + "push %es\n\t" + "push %fs\n\t" + "push %gs\n\t" + /* Set DS == CS */ + "pushw %cs\n\t" + "popw %ds\n\t" + /* Set up parameters for call */ + "movw $" RM_STR(PXENV_UNDI_ISR_IN_START) ", %ds:(undi_isr_FuncFlag-irqstart)\n\t" + "pushw %cs\n\t" + "popw %es\n\t" + "movw $(undi_isr-irqstart), %di\n\t" + "movw $" RM_STR(PXENV_UNDI_ISR) ", %bx\n\t" + "pushw %es\n\t" /* Registers for PXENV+, stack for !PXE */ + "pushw %di\n\t" + "pushw %bx\n\t" + /* Make PXE API call */ + "lcall *%ds:(entry-irqstart)\n\t" + "addw $6, %sp\n\t" + /* Set DS == CS */ + "pushw %cs\n\t" + "popw %ds\n\t" + /* Check return status to see if it's one of our interrupts */ + "cmpw $" RM_STR(PXENV_STATUS_SUCCESS) ", %cs:(undi_isr_Status-irqstart)\n\t" + "jne 1f\n\t" + "cmpw $" RM_STR(PXENV_UNDI_ISR_OUT_OURS) ", %cs:(undi_isr_FuncFlag-irqstart)\n\t" + "jne 1f\n\t" + /* Increment count_ours if so */ + "incw %ds:(count_ours-irqstart)\n\t" + "1:\n\t" + /* Increment count_all anyway */ + "incw %ds:(count_all-irqstart)\n\t" + /* Restore registers and return */ + "popw %gs\n\t" + "popw %fs\n\t" + "popw %es\n\t" + "popw %ds\n\t" + "popal\n\t" + "\n\t" + /* Chain to acknowledge the interrupt */ + "cmpb $0, %cs:(irq_chain-irqstart)\n\t" + "jz 2f\n\t" + "ljmp %cs:(chain_to-irqstart)\n\t" + "2:\n\t" + "\n\t" + "iret\n\t" + "\n\t" + ); + + /* Copy handler */ + memcpy ( target, nontrivial_irq_handler, NONTRIVIAL_IRQ_HANDLER_SIZE ); + + return 1; +} + +static int install_nontrivial_irq_handler ( irq_t irq ) { + undi_irq_handler_t *handler = + &undi.base_mem_data->nontrivial_irq_handler; + segoff_t isr_segoff; + + printf ( "WARNING: using non-trivial IRQ handler [EXPERIMENTAL]\n" ); + /* + * This code is deliberately quick and dirty. The whole + * nontrivial IRQ stuff is only present in order to test out + * calling our PXE stack in interrupt context. Do NOT use + * this in production code. + */ + + disable_irq ( irq ); + handler->count_all = 0; + handler->count_ours = 0; + handler->entry = undi.pxe->EntryPointSP; + nontrivial_irq_previous_trigger_count = 0; + isr_segoff.segment = SEGMENT(handler); + isr_segoff.offset = (void*)&handler->code - (void*)handler; + install_irq_handler( irq, &isr_segoff, + &handler->irq_chain, &handler->chain_to); + enable_irq ( irq ); + + return 1; +} + +static int remove_nontrivial_irq_handler ( irq_t irq ) { + undi_irq_handler_t *handler = + &undi.base_mem_data->nontrivial_irq_handler; + segoff_t isr_segoff; + + isr_segoff.segment = SEGMENT(handler); + isr_segoff.offset = (char*)&handler->code - (char*)handler; + remove_irq_handler( irq, &isr_segoff, + &handler->irq_chain, &handler->chain_to); + return 1; +} + +static int nontrivial_irq_triggered ( irq_t irq __unused ) { + undi_irq_handler_t *handler = + &undi.base_mem_data->nontrivial_irq_handler; + uint16_t nontrivial_irq_this_trigger_count = handler->count_ours; + int triggered = ( nontrivial_irq_this_trigger_count - + nontrivial_irq_previous_trigger_count ); + + nontrivial_irq_previous_trigger_count = + nontrivial_irq_this_trigger_count; + return triggered ? 1 : 0; +} + +static void nontrivial_irq_debug ( irq_t irq ) { + undi_irq_handler_t *handler = + &undi.base_mem_data->nontrivial_irq_handler; + + printf ( "IRQ %d triggered %d times (%d of which were ours)\n", + irq, handler->count_all, handler->count_ours ); +} +#endif /* UNDI_NONTRIVIAL_IRQ */ + +/************************************************************************** + * High-level UNDI API call wrappers + **************************************************************************/ + +/* Install the UNDI driver from a located UNDI ROM. + */ + +static int undi_loader ( void ) { + pxe_t *pxe = NULL; + + if ( ! undi.pci.vendor ) { + printf ( "ERROR: attempted to call loader of an ISA ROM?\n" ); + return 0; + } + + /* AX contains PCI bus:devfn (PCI specification) */ + undi.pxs->loader.ax = ( undi.pci.bus << 8 ) | undi.pci.devfn; + /* BX and DX set to 0xffff for non-ISAPnP devices + * (BIOS boot specification) + */ + undi.pxs->loader.bx = 0xffff; + undi.pxs->loader.dx = 0xffff; + /* ES:DI points to PnP BIOS' $PnP structure + * (BIOS boot specification) + */ + if ( undi.pnp_bios ) { + undi.pxs->loader.es = 0xf000; + undi.pxs->loader.di = virt_to_phys ( undi.pnp_bios ) - 0xf0000; + } else { + /* Set to a NULL pointer and hope that we don't need it */ + undi.pxs->loader.es = 0x0000; + undi.pxs->loader.di = 0x0000; + } + + /* Allocate space for UNDI driver's code and data segments */ + undi.driver_code_size = undi.undi_rom_id->code_size; + undi.driver_code = allot_base_memory ( undi.driver_code_size ); + if ( undi.driver_code == NULL ) { + printf ( "Could not allocate %d bytes for UNDI code segment\n", + undi.driver_code_size ); + return 0; + } + undi.pxs->loader.undi_cs = SEGMENT( undi.driver_code ); + + undi.driver_data_size = undi.undi_rom_id->data_size; + undi.driver_data = allot_base_memory ( undi.driver_data_size ); + if ( undi.driver_data == NULL ) { + printf ( "Could not allocate %d bytes for UNDI code segment\n", + undi.driver_data_size ); + return 0; + } + undi.pxs->loader.undi_ds = SEGMENT( undi.driver_data ); + + printf ( "Installing UNDI driver code to %hx:0000, data at %hx:0000\n", + undi.pxs->loader.undi_cs, undi.pxs->loader.undi_ds ); + + /* Do the API call to install the loader */ + if ( ! undi_call_loader () ) return 0; + + pxe = VIRTUAL( undi.pxs->loader.undi_cs, + undi.pxs->loader.pxe_ptr.offset ); + printf ( "UNDI driver created a pixie at %hx:%hx...", + undi.pxs->loader.undi_cs, undi.pxs->loader.pxe_ptr.offset ); + if ( memcmp ( pxe->Signature, "!PXE", 4 ) != 0 ) { + printf ( "invalid signature\n" ); + return 0; + } + if ( checksum ( pxe, sizeof(pxe_t) ) != 0 ) { + printf ( "invalid checksum\n" ); + return 0; + } + printf ( "ok\n" ); + undi.pxe = pxe; + pxe_dump(); + return 1; +} + +/* Start the UNDI driver. + */ + +static int eb_pxenv_start_undi ( void ) { + int success = 0; + + /* AX contains PCI bus:devfn (PCI specification) */ + undi.pxs->start_undi.ax = ( undi.pci.bus << 8 ) | undi.pci.devfn; + + /* BX and DX set to 0xffff for non-ISAPnP devices + * (BIOS boot specification) + */ + undi.pxs->start_undi.bx = 0xffff; + undi.pxs->start_undi.dx = 0xffff; + /* ES:DI points to PnP BIOS' $PnP structure + * (BIOS boot specification) + */ + if ( undi.pnp_bios ) { + undi.pxs->start_undi.es = 0xf000; + undi.pxs->start_undi.di = + virt_to_phys ( undi.pnp_bios ) - 0xf0000; + } else { + /* Set to a NULL pointer and hope that we don't need it */ + undi.pxs->start_undi.es = 0x0000; + undi.pxs->start_undi.di = 0x0000; + } + + DBG ( "PXENV_START_UNDI => AX=%hx BX=%hx DX=%hx ES:DI=%hx:%hx\n", + undi.pxs->start_undi.ax, + undi.pxs->start_undi.bx, undi.pxs->start_undi.dx, + undi.pxs->start_undi.es, undi.pxs->start_undi.di ); + success = undi_call ( PXENV_START_UNDI ); + DBG ( "PXENV_START_UNDI <= Status=%s\n", UNDI_STATUS(undi.pxs) ); + if ( success ) undi.prestarted = 1; + return success; +} + +static int eb_pxenv_undi_startup ( void ) { + int success = 0; + + DBG ( "PXENV_UNDI_STARTUP => (void)\n" ); + success = undi_call ( PXENV_UNDI_STARTUP ); + DBG ( "PXENV_UNDI_STARTUP <= Status=%s\n", UNDI_STATUS(undi.pxs) ); + if ( success ) undi.started = 1; + return success; +} + +static int eb_pxenv_undi_cleanup ( void ) { + int success = 0; + + DBG ( "PXENV_UNDI_CLEANUP => (void)\n" ); + success = undi_call ( PXENV_UNDI_CLEANUP ); + DBG ( "PXENV_UNDI_CLEANUP <= Status=%s\n", UNDI_STATUS(undi.pxs) ); + return success; +} + +static int eb_pxenv_undi_initialize ( void ) { + int success = 0; + + undi.pxs->undi_initialize.ProtocolIni = 0; + memset ( &undi.pxs->undi_initialize.reserved, 0, + sizeof ( undi.pxs->undi_initialize.reserved ) ); + DBG ( "PXENV_UNDI_INITIALIZE => ProtocolIni=%x\n" ); + success = undi_call ( PXENV_UNDI_INITIALIZE ); + DBG ( "PXENV_UNDI_INITIALIZE <= Status=%s\n", UNDI_STATUS(undi.pxs) ); + if ( success ) undi.initialized = 1; + return success; +} + +static int eb_pxenv_undi_shutdown ( void ) { + int success = 0; + + DBG ( "PXENV_UNDI_SHUTDOWN => (void)\n" ); + success = undi_call ( PXENV_UNDI_SHUTDOWN ); + DBG ( "PXENV_UNDI_SHUTDOWN <= Status=%s\n", UNDI_STATUS(undi.pxs) ); + if ( success ) { + undi.initialized = 0; + undi.started = 0; + } + return success; +} + +static int eb_pxenv_undi_open ( void ) { + int success = 0; + + undi.pxs->undi_open.OpenFlag = 0; + undi.pxs->undi_open.PktFilter = FLTR_DIRECTED | FLTR_BRDCST; + + /* Multicast support not yet implemented */ + undi.pxs->undi_open.R_Mcast_Buf.MCastAddrCount = 0; + DBG ( "PXENV_UNDI_OPEN => OpenFlag=%hx PktFilter=%hx " + "MCastAddrCount=%hx\n", + undi.pxs->undi_open.OpenFlag, undi.pxs->undi_open.PktFilter, + undi.pxs->undi_open.R_Mcast_Buf.MCastAddrCount ); + success = undi_call ( PXENV_UNDI_OPEN ); + DBG ( "PXENV_UNDI_OPEN <= Status=%s\n", UNDI_STATUS(undi.pxs) ); + if ( success ) undi.opened = 1; + return success; +} + +static int eb_pxenv_undi_close ( void ) { + int success = 0; + + DBG ( "PXENV_UNDI_CLOSE => (void)\n" ); + success = undi_call ( PXENV_UNDI_CLOSE ); + DBG ( "PXENV_UNDI_CLOSE <= Status=%s\n", UNDI_STATUS(undi.pxs) ); + if ( success ) undi.opened = 0; + return success; +} + +static int eb_pxenv_undi_transmit_packet ( void ) { + int success = 0; + static const uint8_t broadcast[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF }; + + /* XMitFlag selects unicast / broadcast */ + if ( memcmp ( undi.xmit_data->destaddr, broadcast, + sizeof(broadcast) ) == 0 ) { + undi.pxs->undi_transmit.XmitFlag = XMT_BROADCAST; + } else { + undi.pxs->undi_transmit.XmitFlag = XMT_DESTADDR; + } + + /* Zero reserved dwords */ + undi.pxs->undi_transmit.Reserved[0] = 0; + undi.pxs->undi_transmit.Reserved[1] = 0; + + /* Segment:offset pointer to DestAddr in base memory */ + undi.pxs->undi_transmit.DestAddr.segment = + SEGMENT( undi.xmit_data->destaddr ); + undi.pxs->undi_transmit.DestAddr.offset = + OFFSET( undi.xmit_data->destaddr ); + + /* Segment:offset pointer to TBD in base memory */ + undi.pxs->undi_transmit.TBD.segment = SEGMENT( &undi.xmit_data->tbd ); + undi.pxs->undi_transmit.TBD.offset = OFFSET( &undi.xmit_data->tbd ); + + /* Use only the "immediate" part of the TBD */ + undi.xmit_data->tbd.DataBlkCount = 0; + + DBG ( "PXENV_UNDI_TRANSMIT_PACKET => Protocol=%hx XmitFlag=%hx ...\n" + "... DestAddr=%hx:%hx TBD=%hx:%hx ...\n", + undi.pxs->undi_transmit.Protocol, + undi.pxs->undi_transmit.XmitFlag, + undi.pxs->undi_transmit.DestAddr.segment, + undi.pxs->undi_transmit.DestAddr.offset, + undi.pxs->undi_transmit.TBD.segment, + undi.pxs->undi_transmit.TBD.offset ); + DBG ( "... TBD { ImmedLength=%hx Xmit=%hx:%hx DataBlkCount=%hx }\n", + undi.xmit_data->tbd.ImmedLength, + undi.xmit_data->tbd.Xmit.segment, + undi.xmit_data->tbd.Xmit.offset, + undi.xmit_data->tbd.DataBlkCount ); + success = undi_call ( PXENV_UNDI_TRANSMIT ); + DBG ( "PXENV_UNDI_TRANSMIT_PACKET <= Status=%s\n", + UNDI_STATUS(undi.pxs) ); + return success; +} + +static int eb_pxenv_undi_set_station_address ( void ) { + /* This will spuriously fail on some cards. Ignore failures. + * We only ever use it to set the MAC address to the card's + * permanent value anyway, so it's a useless call (although we + * make it because PXE spec says we should). + */ + DBG ( "PXENV_UNDI_SET_STATION_ADDRESS => " + "StationAddress=%!\n", + undi.pxs->undi_set_station_address.StationAddress ); + undi_call_silent ( PXENV_UNDI_SET_STATION_ADDRESS ); + DBG ( "PXENV_UNDI_SET_STATION_ADDRESS <= Status=%s\n", + UNDI_STATUS(undi.pxs) ); + return 1; +} + +static int eb_pxenv_undi_get_information ( void ) { + int success = 0; + memset ( undi.pxs, 0, sizeof ( undi.pxs ) ); + DBG ( "PXENV_UNDI_GET_INFORMATION => (void)\n" ); + success = undi_call ( PXENV_UNDI_GET_INFORMATION ); + DBG ( "PXENV_UNDI_GET_INFORMATION <= Status=%s " + "BaseIO=%hx IntNumber=%hx ...\n" + "... MaxTranUnit=%hx HwType=%hx HwAddrlen=%hx ...\n" + "... CurrentNodeAddress=%! PermNodeAddress=%! ...\n" + "... ROMAddress=%hx RxBufCt=%hx TxBufCt=%hx\n", + UNDI_STATUS(undi.pxs), + undi.pxs->undi_get_information.BaseIo, + undi.pxs->undi_get_information.IntNumber, + undi.pxs->undi_get_information.MaxTranUnit, + undi.pxs->undi_get_information.HwType, + undi.pxs->undi_get_information.HwAddrLen, + undi.pxs->undi_get_information.CurrentNodeAddress, + undi.pxs->undi_get_information.PermNodeAddress, + undi.pxs->undi_get_information.ROMAddress, + undi.pxs->undi_get_information.RxBufCt, + undi.pxs->undi_get_information.TxBufCt ); + return success; +} + +static int eb_pxenv_undi_get_iface_info ( void ) { + int success = 0; + + DBG ( "PXENV_UNDI_GET_IFACE_INFO => (void)\n" ); + success = undi_call ( PXENV_UNDI_GET_IFACE_INFO ); + DBG ( "PXENV_UNDI_GET_IFACE_INFO <= Status=%s IfaceType=%s ...\n" + "... LinkSpeed=%x ServiceFlags=%x\n", + UNDI_STATUS(undi.pxs), + undi.pxs->undi_get_iface_info.IfaceType, + undi.pxs->undi_get_iface_info.LinkSpeed, + undi.pxs->undi_get_iface_info.ServiceFlags ); + return success; +} + +static int eb_pxenv_undi_isr ( void ) { + int success = 0; + + DBG ( "PXENV_UNDI_ISR => FuncFlag=%hx\n", + undi.pxs->undi_isr.FuncFlag ); + success = undi_call ( PXENV_UNDI_ISR ); + DBG ( "PXENV_UNDI_ISR <= Status=%s FuncFlag=%hx BufferLength=%hx ...\n" + "... FrameLength=%hx FrameHeaderLength=%hx Frame=%hx:%hx " + "ProtType=%hhx ...\n... PktType=%hhx\n", + UNDI_STATUS(undi.pxs), undi.pxs->undi_isr.FuncFlag, + undi.pxs->undi_isr.BufferLength, + undi.pxs->undi_isr.FrameLength, + undi.pxs->undi_isr.FrameHeaderLength, + undi.pxs->undi_isr.Frame.segment, + undi.pxs->undi_isr.Frame.offset, + undi.pxs->undi_isr.ProtType, + undi.pxs->undi_isr.PktType ); + return success; +} + +static int eb_pxenv_stop_undi ( void ) { + int success = 0; + + DBG ( "PXENV_STOP_UNDI => (void)\n" ); + success = undi_call ( PXENV_STOP_UNDI ); + DBG ( "PXENV_STOP_UNDI <= Status=%s\n", UNDI_STATUS(undi.pxs) ); + if ( success ) undi.prestarted = 0; + return success; +} + +static int eb_pxenv_unload_stack ( void ) { + int success = 0; + + memset ( undi.pxs, 0, sizeof ( undi.pxs ) ); + DBG ( "PXENV_UNLOAD_STACK => (void)\n" ); + success = undi_call_silent ( PXENV_UNLOAD_STACK ); + DBG ( "PXENV_UNLOAD_STACK <= Status=%s ...\n... (%s)\n", + UNDI_STATUS(undi.pxs), + ( undi.pxs->Status == PXENV_STATUS_SUCCESS ? + "base-code is ready to be removed" : + ( undi.pxs->Status == PXENV_STATUS_FAILURE ? + "the size of free base memory has been changed" : + ( undi.pxs->Status == PXENV_STATUS_KEEP_ALL ? + "the NIC interrupt vector has been changed" : + "UNEXPECTED STATUS CODE" ) ) ) ); + return success; +} + +static int eb_pxenv_stop_base ( void ) { + int success = 0; + + DBG ( "PXENV_STOP_BASE => (void)\n" ); + success = undi_call ( PXENV_STOP_BASE ); + DBG ( "PXENV_STOP_BASE <= Status=%s\n", UNDI_STATUS(undi.pxs) ); + return success; +} + +/* Unload UNDI base code (if any present) and free memory. + */ +static int undi_unload_base_code ( void ) { + void *bc_code = VIRTUAL( undi.pxe->BC_Code.Seg_Addr, 0 ); + size_t bc_code_size = undi.pxe->BC_Code.Seg_Size; + void *bc_data = VIRTUAL( undi.pxe->BC_Data.Seg_Addr, 0 ); + size_t bc_data_size = undi.pxe->BC_Data.Seg_Size; + void *bc_stck = VIRTUAL( undi.pxe->Stack.Seg_Addr, 0 ); + size_t bc_stck_size = undi.pxe->Stack.Seg_Size; + firing_squad_lineup_t lineup; + + /* Since we never start the base code, the only time we should + * reach this is if we were loaded via PXE. There are many + * different and conflicting versions of the "correct" way to + * unload the PXE base code, several of which appear within + * the PXE specification itself. This one seems to work for + * our purposes. + * + * We always call PXENV_STOP_BASE and PXENV_UNLOAD_STACK even + * if the !PXE structure indicates that no base code is + * present. We do this for the case that there is a + * base-code-less UNDI driver loaded that has hooked some + * interrupts. If the base code really is absent, then these + * calls will fail, we will ignore the failure, and our + * subsequent memory-freeing code is robust enough to handle + * whatever's thrown at it. + */ + eb_pxenv_stop_base(); + eb_pxenv_unload_stack(); + if ( ( undi.pxs->unload_stack.Status != PXENV_STATUS_SUCCESS ) && + ( undi.pxs->unload_stack.Status != PXENV_STATUS_FAILURE ) && + ( undi.pxe->BC_Code.Seg_Addr != 0 ) ) + { + printf ( "Could not free memory allocated to PXE base code: " + "possible memory leak\n" ); + return 0; + } + /* Free data structures. Forget what the PXE specification + * says about how to calculate the new size of base memory; + * basemem.c takes care of all that for us. Note that we also + * have to free the stack (even though PXE spec doesn't say + * anything about it) because nothing else is going to do so. + * + * Structures will almost certainly not be kB-aligned and + * there's a reasonable chance that the UNDI code or data + * portions will lie in the same kB as the base code. Since + * forget_base_memory works only in 1kB increments, this means + * we have to do some arcane trickery. + */ + memset ( &lineup, 0, sizeof(lineup) ); + if ( SEGMENT(bc_code) != 0 ) + assemble_firing_squad( &lineup, bc_code, bc_code_size, SHOOT ); + if ( SEGMENT(bc_data) != 0 ) + assemble_firing_squad( &lineup, bc_data, bc_data_size, SHOOT ); + if ( SEGMENT(bc_stck) != 0 ) + assemble_firing_squad( &lineup, bc_stck, bc_stck_size, SHOOT ); + /* Don't shoot any bits of the UNDI driver code or data */ + assemble_firing_squad ( &lineup, + VIRTUAL(undi.pxe->UNDICode.Seg_Addr, 0), + undi.pxe->UNDICode.Seg_Size, DONTSHOOT ); + assemble_firing_squad ( &lineup, + VIRTUAL(undi.pxe->UNDIData.Seg_Addr, 0), + undi.pxe->UNDIData.Seg_Size, DONTSHOOT ); + shoot_targets ( &lineup ); + undi.pxe->BC_Code.Seg_Addr = 0; + undi.pxe->BC_Data.Seg_Addr = 0; + undi.pxe->Stack.Seg_Addr = 0; + + /* Free and reallocate our own base memory data structures, to + * allow the freed base-code blocks to be fully released. + */ + free_base_mem_data(); + if ( ! allocate_base_mem_data() ) { + printf ( "FATAL: memory unaccountably lost\n" ); + while ( 1 ) {}; + } + + return 1; +} + +/* UNDI full initialization + * + * This calls all the various UNDI initialization routines in sequence. + */ + +static int undi_full_startup ( void ) { + if ( ! eb_pxenv_start_undi() ) return 0; + if ( ! eb_pxenv_undi_startup() ) return 0; + if ( ! eb_pxenv_undi_initialize() ) return 0; + if ( ! eb_pxenv_undi_get_information() ) return 0; + undi.irq = undi.pxs->undi_get_information.IntNumber; + copy_undi_irq_handler ( undi.base_mem_data->irq_handler, + UNDI_IRQ_HANDLER_SIZE ); + if ( ! install_undi_irq_handler ( undi.irq ) ) { + undi.irq = IRQ_NONE; + return 0; + } + memmove ( &undi.pxs->undi_set_station_address.StationAddress, + &undi.pxs->undi_get_information.PermNodeAddress, + sizeof (undi.pxs->undi_set_station_address.StationAddress) ); + if ( ! eb_pxenv_undi_set_station_address() ) return 0; + if ( ! eb_pxenv_undi_open() ) return 0; + return 1; +} + +/* UNDI full shutdown + * + * This calls all the various UNDI shutdown routines in sequence and + * also frees any memory that it can. + */ + +static int undi_full_shutdown ( void ) { + if ( undi.pxe != NULL ) { + /* In case we didn't allocate the driver's memory in the first + * place, try to grab the code and data segments and sizes + * from the !PXE structure. + */ + if ( undi.driver_code == NULL ) { + undi.driver_code = VIRTUAL(undi.pxe->UNDICode.Seg_Addr, + 0 ); + undi.driver_code_size = undi.pxe->UNDICode.Seg_Size; + } + if ( undi.driver_data == NULL ) { + undi.driver_data = VIRTUAL(undi.pxe->UNDIData.Seg_Addr, + 0 ); + undi.driver_data_size = undi.pxe->UNDIData.Seg_Size; + } + + /* Ignore errors and continue in the hope of shutting + * down anyway + */ + if ( undi.opened ) eb_pxenv_undi_close(); + if ( undi.started ) { + eb_pxenv_undi_cleanup(); + /* We may get spurious UNDI API errors at this + * point. If startup() succeeded but + * initialize() failed then according to the + * spec, we should call shutdown(). However, + * some NICS will fail with a status code + * 0x006a (INVALID_STATE). + */ + eb_pxenv_undi_shutdown(); + } + if ( undi.irq != IRQ_NONE ) { + remove_undi_irq_handler ( undi.irq ); + undi.irq = IRQ_NONE; + } + undi_unload_base_code(); + if ( undi.prestarted ) { + eb_pxenv_stop_undi(); + /* Success OR Failure indicates that memory + * can be freed. Any other status code means + * that it can't. + */ + if (( undi.pxs->Status == PXENV_STATUS_KEEP_UNDI ) || + ( undi.pxs->Status == PXENV_STATUS_KEEP_ALL ) ) { + printf ("Could not free memory allocated to " + "UNDI driver: possible memory leak\n"); + return 0; + } + } + } + /* Free memory allocated to UNDI driver */ + if ( undi.driver_code != NULL ) { + /* Clear contents in order to eliminate !PXE and PXENV + * signatures to prevent spurious detection via base + * memory scan. + */ + memset ( undi.driver_code, 0, undi.driver_code_size ); + forget_base_memory ( undi.driver_code, undi.driver_code_size ); + undi.driver_code = NULL; + undi.driver_code_size = 0; + } + if ( undi.driver_data != NULL ) { + forget_base_memory ( undi.driver_data, undi.driver_data_size ); + undi.driver_data = NULL; + undi.driver_data_size = 0; + } + /* !PXE structure now gone; memory freed */ + undi.pxe = NULL; + return 1; +} + +/************************************************************************** +POLL - Wait for a frame +***************************************************************************/ +static int undi_poll(struct nic *nic, int retrieve) +{ + /* Fun, fun, fun. UNDI drivers don't use polling; they use + * interrupts. We therefore cheat and pretend that an + * interrupt has occurred every time undi_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 + * undi_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. + */ + + /* See if a hardware interrupt has occurred since the last poll(). + */ + if ( ! undi_irq_triggered ( undi.irq ) ) return 0; + + /* Given the frailty of PXE stacks, it's probably not safe to + * risk calling PXENV_UNDI_ISR with + * FuncFlag=PXENV_UNDI_ISR_START twice for the same interrupt, + * so we cheat slightly and assume that there is something + * ready to retrieve as long as an interrupt has occurred. + */ + if ( ! retrieve ) return 1; + +#ifdef UNDI_NONTRIVIAL_IRQ + /* With the nontrivial IRQ handler, we have already called + * PXENV_UNDI_ISR with PXENV_UNDI_ISR_IN_START and determined + * that it is one of ours. + */ +#else + /* Ask the UNDI driver if this is "our" interrupt. + */ + undi.pxs->undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_START; + if ( ! eb_pxenv_undi_isr() ) return 0; + if ( undi.pxs->undi_isr.FuncFlag == PXENV_UNDI_ISR_OUT_NOT_OURS ) { + /* "Not our interrupt" translates to "no packet ready + * to read". + */ + /* FIXME: Technically, we shouldn't be the one sending + * EOI. However, since our IRQ handlers don't yet + * support chaining, nothing else gets the chance to. + * One nice side-effect of doing this is that it means + * we can cheat and claim the timer interrupt as our + * NIC interrupt; it will be inefficient but will + * work. + */ + send_specific_eoi ( undi.irq ); + return 0; + } +#endif + + /* At this stage, the device should have cleared its interrupt + * line so we can send EOI to the 8259. + */ + send_specific_eoi ( undi.irq ); + + /* We might have received a packet, or this might be a + * "transmit completed" interrupt. Zero nic->packetlen, + * increment whenever we receive a bit of a packet, test + * nic->packetlen when we're done to see whether or not we + * actually received anything. + */ + nic->packetlen = 0; + undi.pxs->undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_PROCESS; + if ( ! eb_pxenv_undi_isr() ) return 0; + while ( undi.pxs->undi_isr.FuncFlag != PXENV_UNDI_ISR_OUT_DONE ) { + switch ( undi.pxs->undi_isr.FuncFlag ) { + case PXENV_UNDI_ISR_OUT_TRANSMIT: + /* We really don't care about transmission complete + * interrupts. + */ + break; + case PXENV_UNDI_ISR_OUT_BUSY: + /* This should never happen. + */ + printf ( "UNDI ISR thinks it's being re-entered!\n" + "Aborting receive\n" ); + return 0; + case PXENV_UNDI_ISR_OUT_RECEIVE: + /* Copy data to receive buffer */ + memcpy ( nic->packet + nic->packetlen, + VIRTUAL( undi.pxs->undi_isr.Frame.segment, + undi.pxs->undi_isr.Frame.offset ), + undi.pxs->undi_isr.BufferLength ); + nic->packetlen += undi.pxs->undi_isr.BufferLength; + break; + default: + printf ( "UNDI ISR returned bizzare status code %d\n", + undi.pxs->undi_isr.FuncFlag ); + } + undi.pxs->undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT; + if ( ! eb_pxenv_undi_isr() ) return 0; + } + return nic->packetlen > 0 ? 1 : 0; +} + +/************************************************************************** +TRANSMIT - Transmit a frame +***************************************************************************/ +static void undi_transmit( + struct nic *nic __unused, + const char *d, /* Destination */ + unsigned int t, /* Type */ + unsigned int s, /* size */ + const char *p) /* Packet */ +{ + /* Copy destination to buffer in base memory */ + memcpy ( undi.xmit_data->destaddr, d, sizeof(MAC_ADDR) ); + + /* Translate packet type to UNDI packet type */ + switch ( t ) { + case IP : undi.pxs->undi_transmit.Protocol = P_IP; break; + case ARP: undi.pxs->undi_transmit.Protocol = P_ARP; break; + case RARP: undi.pxs->undi_transmit.Protocol = P_RARP; break; + default: printf ( "Unknown packet type %hx\n", t ); + return; + } + + /* Store packet length in TBD */ + undi.xmit_data->tbd.ImmedLength = s; + + /* Check to see if data to be transmitted is currently in base + * memory. If not, allocate temporary storage in base memory + * and copy it there. + */ + if ( SEGMENT( p ) <= 0xffff ) { + undi.xmit_data->tbd.Xmit.segment = SEGMENT( p ); + undi.xmit_data->tbd.Xmit.offset = OFFSET( p ); + } else { + memcpy ( undi.xmit_buffer, p, s ); + undi.xmit_data->tbd.Xmit.segment = SEGMENT( undi.xmit_buffer ); + undi.xmit_data->tbd.Xmit.offset = OFFSET( undi.xmit_buffer ); + } + + eb_pxenv_undi_transmit_packet(); +} + +/************************************************************************** +DISABLE - Turn off ethernet interface +***************************************************************************/ +static void undi_disable ( struct dev *dev __unused ) { + undi_full_shutdown(); + free_base_mem_data(); +} + +/************************************************************************** +PROBE - Look for an adapter, this routine's visible to the outside +***************************************************************************/ + +/* Locate an UNDI driver by first scanning through base memory for an + * installed driver and then by scanning for UNDI ROMs and attempting + * to install their drivers. + */ + +static int hunt_pixies_and_undi_roms ( void ) { + static uint8_t hunt_type = HUNT_FOR_PIXIES; + + if ( hunt_type == HUNT_FOR_PIXIES ) { + if ( hunt_pixie() ) { + return 1; + } + } + hunt_type = HUNT_FOR_UNDI_ROMS; + while ( hunt_undi_rom() ) { + if ( undi_loader() ) { + return 1; + } + undi_full_shutdown(); /* Free any allocated memory */ + } + hunt_type = HUNT_FOR_PIXIES; + return 0; +} + +/* The actual Etherboot probe routine. + */ + +static int undi_probe(struct dev *dev, struct pci_device *pci) +{ + struct nic *nic = (struct nic *)dev; + + /* Zero out global undi structure */ + memset ( &undi, 0, sizeof(undi) ); + + /* Store PCI parameters; we will need them to initialize the + * UNDI driver later. If not a PCI device, leave as 0. + */ + if ( pci ) { + memcpy ( &undi.pci, pci, sizeof(undi.pci) ); + } + + /* Find the BIOS' $PnP structure */ + if ( ! hunt_pnp_bios() ) { + /* Not all PXE stacks actually insist on a PnP BIOS. + * In particular, an Etherboot PXE stack will work + * just fine without one. + * + * We used to make this a fatal error, but now we just + * warn and continue. Note that this is necessary in + * order to be able to debug the Etherboot PXE stack + * under Bochs, since Bochs' BIOS is non-PnP. + */ + printf ( "WARNING: No PnP BIOS found\n" ); + } + + /* Allocate base memory data structures */ + if ( ! allocate_base_mem_data() ) return 0; + + /* Search thoroughly for UNDI drivers */ + for ( ; hunt_pixies_and_undi_roms(); undi_full_shutdown() ) { + /* Try to initialise UNDI driver */ + printf ( "Initializing UNDI driver. Please wait...\n" ); + if ( ! undi_full_startup() ) { + if ( undi.pxs->Status == + PXENV_STATUS_UNDI_MEDIATEST_FAILED ) { + printf ( "Cable not connected (code %#hx)\n", + PXENV_STATUS_UNDI_MEDIATEST_FAILED ); + } + continue; + } + /* Basic information: MAC, IO addr, IRQ */ + if ( ! eb_pxenv_undi_get_information() ) continue; + printf ( "Initialized UNDI NIC with IO %#hx, IRQ %d, MAC %!\n", + undi.pxs->undi_get_information.BaseIo, + undi.pxs->undi_get_information.IntNumber, + undi.pxs->undi_get_information.CurrentNodeAddress ); + /* Fill out MAC address in nic structure */ + memcpy ( nic->node_addr, + undi.pxs->undi_get_information.CurrentNodeAddress, + ETH_ALEN ); + /* More diagnostic information including link speed */ + if ( ! eb_pxenv_undi_get_iface_info() ) continue; + printf ( "NDIS type %s interface at %d Mbps\n", + undi.pxs->undi_get_iface_info.IfaceType, + undi.pxs->undi_get_iface_info.LinkSpeed / 1000000 ); + dev->disable = undi_disable; + nic->poll = undi_poll; + nic->transmit = undi_transmit; + return 1; + } + undi_disable ( dev ); /* To free base memory structures */ + return 0; +} + +static int undi_isa_probe ( struct dev *dev, + unsigned short *probe_addrs __unused ) { + return undi_probe ( dev, NULL ); +} + + +/* UNDI driver states that it is suitable for any PCI NIC (i.e. any + * PCI device of class PCI_CLASS_NETWORK_ETHERNET). If there are any + * obscure UNDI NICs that have the incorrect PCI class, add them to + * this list. + */ +static struct pci_id undi_nics[] = { + /* PCI_ROM(0x0000, 0x0000, "undi", "UNDI adaptor"), */ +}; + +static struct pci_driver undi_driver __pci_driver = { + .type = NIC_DRIVER, + .name = "UNDI", + .probe = undi_probe, + .ids = undi_nics, + .id_count = sizeof(undi_nics)/sizeof(undi_nics[0]), + .class = PCI_CLASS_NETWORK_ETHERNET, +}; + +static struct isa_driver undi_isa_driver __isa_driver = { + .type = NIC_DRIVER, + .name = "UNDI", + .probe = undi_isa_probe, + .ioaddrs = 0, +}; + +#endif /* PCBIOS */ diff --git a/src/arch/i386/drivers/net/undi.h b/src/arch/i386/drivers/net/undi.h new file mode 100644 index 00000000..3ba76c35 --- /dev/null +++ b/src/arch/i386/drivers/net/undi.h @@ -0,0 +1,178 @@ +/************************************************************************** +Etherboot - BOOTP/TFTP Bootstrap Program +UNDI NIC driver for Etherboot - header file + +This file Copyright (C) 2003 Michael Brown +of Fen Systems Ltd. (http://www.fensystems.co.uk/). All rights +reserved. + +$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 "pxe.h" +#include "pic8259.h" + +/* A union that can function as the parameter block for any UNDI API call. + */ +typedef t_PXENV_ANY pxenv_structure_t; + +/* BIOS PnP parameter block. We scan for this so that we can pass it + * to the UNDI driver. + */ + +#define PNP_BIOS_SIGNATURE ( ('$'<<0) + ('P'<<8) + ('n'<<16) + ('P'<<24) ) +typedef struct pnp_bios { + uint32_t signature; + uint8_t version; + uint8_t length; + uint16_t control; + uint8_t checksum; + uint8_t dontcare[24]; +} PACKED pnp_bios_t; + +/* Structures within the PXE ROM. + */ + +#define ROM_SIGNATURE 0xaa55 +typedef struct rom { + uint16_t signature; + uint8_t unused[0x14]; + uint16_t undi_rom_id_off; + uint16_t pcir_off; + uint16_t pnp_off; +} PACKED rom_t; + +#define PCIR_SIGNATURE ( ('P'<<0) + ('C'<<8) + ('I'<<16) + ('R'<<24) ) +typedef struct pcir_header { + uint32_t signature; + uint16_t vendor_id; + uint16_t device_id; +} PACKED pcir_header_t; + +#define PNP_SIGNATURE ( ('$'<<0) + ('P'<<8) + ('n'<<16) + ('P'<<24) ) +typedef struct pnp_header { + uint32_t signature; + uint8_t struct_revision; + uint8_t length; + uint16_t next; + uint8_t reserved; + uint8_t checksum; + uint16_t id[2]; + uint16_t manuf_str_off; + uint16_t product_str_off; + uint8_t base_type; + uint8_t sub_type; + uint8_t interface_type; + uint8_t indicator; + uint16_t boot_connect_off; + uint16_t disconnect_off; + uint16_t initialise_off; + uint16_t reserved2; + uint16_t info; +} PACKED pnp_header_t; + +#define UNDI_SIGNATURE ( ('U'<<0) + ('N'<<8) + ('D'<<16) + ('I'<<24) ) +typedef struct undi_rom_id { + uint32_t signature; + uint8_t struct_length; + uint8_t struct_cksum; + uint8_t struct_rev; + uint8_t undi_rev[3]; + uint16_t undi_loader_off; + uint16_t stack_size; + uint16_t data_size; + uint16_t code_size; +} PACKED undi_rom_id_t; + +/* Nontrivial IRQ handler structure */ +typedef struct { + segoff_t chain_to; + uint8_t irq_chain, pad1, pad2, pad3; + segoff_t entry; + uint16_t count_all; + uint16_t count_ours; + t_PXENV_UNDI_ISR undi_isr; + char code[0]; +} PACKED undi_irq_handler_t ; + +/* Storage buffers that we need in base memory. We collect these into + * a single structure to make allocation simpler. + */ + +typedef struct undi_base_mem_xmit_data { + MAC_ADDR destaddr; + t_PXENV_UNDI_TBD tbd; +} undi_base_mem_xmit_data_t; + +typedef struct undi_base_mem_data { + pxenv_structure_t pxs; + undi_base_mem_xmit_data_t xmit_data; + char xmit_buffer[ETH_FRAME_LEN]; + /* Must be last in structure and paragraph-aligned */ + union { + char e820mangler[0]; + char irq_handler[0]; + undi_irq_handler_t nontrivial_irq_handler; + } __attribute__ ((aligned(16))); +} undi_base_mem_data_t; + +/* Macros and data structures used when freeing bits of base memory + * used by the UNDI driver. + */ + +#define FIRING_SQUAD_TARGET_SIZE 8 +#define FIRING_SQUAD_TARGET_INDEX(x) ( (x) / FIRING_SQUAD_TARGET_SIZE ) +#define FIRING_SQUAD_TARGET_BIT(x) ( (x) % FIRING_SQUAD_TARGET_SIZE ) +typedef struct firing_squad_lineup { + uint8_t targets[ 640 / FIRING_SQUAD_TARGET_SIZE ]; +} firing_squad_lineup_t; +typedef enum firing_squad_shoot { + DONTSHOOT = 0, + SHOOT = 1 +} firing_squad_shoot_t; + +/* Driver private data structure. + */ + +typedef struct undi { + /* Pointers to various data structures */ + pnp_bios_t *pnp_bios; + rom_t *rom; + undi_rom_id_t *undi_rom_id; + pxe_t *pxe; + pxenv_structure_t *pxs; + undi_base_mem_xmit_data_t *xmit_data; + /* Pointers and sizes to keep track of allocated base memory */ + undi_base_mem_data_t *base_mem_data; + void *driver_code; + size_t driver_code_size; + void *driver_data; + size_t driver_data_size; + char *xmit_buffer; + /* Flags. We keep our own instead of trusting the UNDI driver + * to have implemented PXENV_UNDI_GET_STATE correctly. Plus + * there's the small issue of PXENV_UNDI_GET_STATE being the + * same API call as PXENV_STOP_UNDI... + */ + uint8_t prestarted; /* pxenv_start_undi() has been called */ + uint8_t started; /* pxenv_undi_startup() has been called */ + uint8_t initialized; /* pxenv_undi_initialize() has been called */ + uint8_t opened; /* pxenv_undi_open() has been called */ + /* Parameters that we need to store for future reference + */ + struct pci_device pci; + irq_t irq; +} undi_t; + +/* Constants + */ + +#define HUNT_FOR_PIXIES 0 +#define HUNT_FOR_UNDI_ROMS 1 diff --git a/src/arch/i386/firmware/pcbios/basemem.c b/src/arch/i386/firmware/pcbios/basemem.c new file mode 100644 index 00000000..c93b19e9 --- /dev/null +++ b/src/arch/i386/firmware/pcbios/basemem.c @@ -0,0 +1,317 @@ +#ifdef PCBIOS + +#include "etherboot.h" +#include "realmode.h" /* for real_mode_stack */ + +/* Routines to allocate base memory in a BIOS-compatible way, by + * updating the Free Base Memory Size counter at 40:13h. + * + * Michael Brown (mcb30) + * $Id$ + */ + +#define fbms ( ( uint16_t * ) phys_to_virt ( 0x413 ) ) +#define BASE_MEMORY_MAX ( 640 ) +#define FREE_BLOCK_MAGIC ( ('!'<<0) + ('F'<<8) + ('R'<<16) + ('E'<<24) ) +#define FREE_BASE_MEMORY ( (uint32_t) ( *fbms << 10 ) ) + +/* Prototypes */ +void * _allot_base_memory ( size_t size ); +void _forget_base_memory ( void *ptr, size_t size ); + +typedef struct free_base_memory_block { + uint32_t magic; + uint16_t size_kb; +} free_base_memory_block_t; + +/* Return amount of free base memory in bytes + */ + +uint32_t get_free_base_memory ( void ) { + return FREE_BASE_MEMORY; +} + +/* Start of our image in base memory. + */ +#define __text16_nocompress __attribute__ ((section (".text16.nocompress"))) +uint32_t image_basemem __text16_nocompress = 0; +uint32_t image_basemem_size __text16_nocompress = 0; + +/* Allot/free the real-mode stack + */ + +void allot_real_mode_stack ( void ) +{ + void *new_real_mode_stack; + + if ( lock_real_mode_stack ) + return; + + /* This is evil hack. + * Until we have a real_mode stack use 0x7c00. + * Except for 0 - 0x600 membory below 0x7c00 is hardly every used. + * This stack should never be used unless the stack allocation fails, + * or if someone has placed a print statement in a dangerous location. + */ + if (!real_mode_stack) { + real_mode_stack = 0x7c00; + } + new_real_mode_stack = _allot_base_memory ( real_mode_stack_size ); + if ( ! new_real_mode_stack ) { + printf ( "FATAL: No real-mode stack\n" ); + while ( 1 ) {}; + } + real_mode_stack = virt_to_phys ( new_real_mode_stack ); + get_memsizes(); +} + +void forget_real_mode_stack ( void ) +{ + if ( lock_real_mode_stack ) + return; + + if ( real_mode_stack) { + _forget_base_memory ( phys_to_virt(real_mode_stack), + real_mode_stack_size ); + /* get_memsizes() uses the real_mode stack we just freed + * for it's BIOS calls. + */ + get_memsizes(); + real_mode_stack = 0; + } +} + +/* Allocate N bytes of base memory. Amount allocated will be rounded + * up to the nearest kB, since that's the granularity of the BIOS FBMS + * counter. Returns NULL if memory cannot be allocated. + */ + +static void * _allot_base_memory ( size_t size ) +{ + uint16_t size_kb = ( size + 1023 ) >> 10; + void *ptr = NULL; + +#ifdef DEBUG_BASEMEM + printf ( "Trying to allocate %d kB of base memory from %d kB free\n", + size_kb, *fbms ); +#endif + + /* Free up any unused memory before we start */ + free_unused_base_memory(); + + /* Check available base memory */ + if ( size_kb > *fbms ) { return NULL; } + + /* Reduce available base memory */ + *fbms -= size_kb; + + /* Calculate address of memory allocated */ + ptr = phys_to_virt ( FREE_BASE_MEMORY ); + + /* Zero out memory. We do this so that allocation of + * already-used space will show up in the form of a crash as + * soon as possible. + * + * Update: there's another reason for doing this. If we don't + * zero the contents, then they could still retain our "free + * block" markers and be liable to being freed whenever a + * base-memory allocation routine is next called. + */ + memset ( ptr, 0, size_kb << 10 ); + +#ifdef DEBUG_BASEMEM + printf ( "Allocated %d kB at [%x,%x)\n", size_kb, + virt_to_phys ( ptr ), + virt_to_phys ( ptr ) + size_kb * 1024 ); +#endif + + return ptr; +} + +void * allot_base_memory ( size_t size ) +{ + void *ptr; + + /* Free real-mode stack, allocate memory, reallocate real-mode + * stack. + */ + forget_real_mode_stack(); + ptr = _allot_base_memory ( size ); + get_memsizes(); + return ptr; +} + +/* Free base memory allocated by allot_base_memory. The BIOS provides + * nothing better than a LIFO mechanism for freeing memory (i.e. it + * just has the single "total free memory" counter), but we improve + * upon this slightly; as long as you free all the allotted blocks, it + * doesn't matter what order you free them in. (This will only work + * for blocks that are freed via forget_base_memory()). + * + * Yes, it's annoying that you have to remember the size of the blocks + * you've allotted. However, since our granularity of allocation is + * 1K, the alternative is to risk wasting the occasional kB of base + * memory, which is a Bad Thing. Really, you should be using as + * little base memory as possible, so consider the awkwardness of the + * API to be a feature! :-) + */ + +static void _forget_base_memory ( void *ptr, size_t size ) +{ + uint16_t remainder = virt_to_phys(ptr) & 1023; + uint16_t size_kb = ( size + remainder + 1023 ) >> 10; + free_base_memory_block_t *free_block = + ( free_base_memory_block_t * ) ( ptr - remainder ); + + if ( ( ptr == NULL ) || ( size == 0 ) ) { + return; + } + +#ifdef DEBUG_BASEMEM + printf ( "Trying to free %d bytes base memory at 0x%x\n", + size, virt_to_phys ( ptr ) ); + if ( remainder > 0 ) { + printf ( "WARNING: destructively expanding free block " + "downwards to 0x%x\n", + virt_to_phys ( ptr - remainder ) ); + } +#endif + + /* Mark every kilobyte within this block as free. This is + * overkill for normal purposes, but helps when something has + * allocated base memory with a granularity finer than the + * BIOS granularity of 1kB. PXE ROMs tend to do this when + * they allocate their own memory. This method allows us to + * free their blocks (admittedly in a rather dangerous, + * tread-on-anything-either-side sort of way, but there's no + * other way to do it). + * + * Since we're marking every kB as free, there's actually no + * need for recording the size of the blocks. However, we + * keep this in so that debug messages are friendlier. It + * probably adds around 8 bytes to the overall code size. + */ + while ( size_kb > 0 ) { + /* Mark this block as unused */ + free_block->magic = FREE_BLOCK_MAGIC; + free_block->size_kb = size_kb; + /* Move up by 1 kB */ + free_block = (void *)(((char *)free_block) + (1 << 10)); + size_kb--; + } + + /* Free up unused base memory */ + free_unused_base_memory(); +} + +void forget_base_memory ( void *ptr, size_t size ) +{ + /* Free memory, free real-mode stack, re-allocate real-mode + * stack. Do this so that we don't end up wasting a huge + * block of memory trapped behind the real-mode stack. + */ + _forget_base_memory ( ptr, size ); + forget_real_mode_stack(); + get_memsizes(); +} + +/* Do the actual freeing of memory. This is split out from + * forget_base_memory() so that it may be called separately. It + * should be called whenever base memory is deallocated by an external + * entity (if we can detect that it has done so) so that we get the + * chance to free up our own blocks. + */ +static void free_unused_base_memory ( void ) { + free_base_memory_block_t *free_block = NULL; + + /* Try to release memory back to the BIOS. Free all + * consecutive blocks marked as free. + */ + while ( 1 ) { + /* Calculate address of next potential free block */ + free_block = ( free_base_memory_block_t * ) + phys_to_virt ( FREE_BASE_MEMORY ); + + /* Stop processing if we're all the way up to 640K or + * if this is not a free block + */ + if ( ( *fbms == BASE_MEMORY_MAX ) || + ( free_block->magic != FREE_BLOCK_MAGIC ) ) { + break; + } + + /* Return memory to BIOS */ + *fbms += free_block->size_kb; + +#ifdef DEBUG_BASEMEM + printf ( "Freed %d kB base memory, %d kB now free\n", + free_block->size_kb, *fbms ); +#endif + + /* Zero out freed block. We do this in case + * the block contained any structures that + * might be located by scanning through + * memory. + */ + memset ( free_block, 0, free_block->size_kb << 10 ); + } +} + +/* Free base memory used by the prefix. Called once at start of + * Etherboot by arch_main(). + */ +void forget_prefix_base_memory ( void ) +{ + /* runtime_start_kb is _text rounded down to a physical kB boundary */ + uint32_t runtime_start_kb = virt_to_phys(_text) & ~0x3ff; + /* prefix_size_kb is the prefix size excluding any portion + * that overlaps into the first kB used by the runtime image + */ + uint32_t prefix_size_kb = runtime_start_kb - image_basemem; + +#ifdef DEBUG_BASEMEM + printf ( "Attempting to free base memory used by prefix\n" ); +#endif + + /* If the decompressor is in allocated base memory + * *and* the Etherboot text is in base + * memory, then free the decompressor. + */ + if ( ( image_basemem >= FREE_BASE_MEMORY ) && + ( runtime_start_kb >= FREE_BASE_MEMORY ) && + ( runtime_start_kb <= ( BASE_MEMORY_MAX << 10 ) ) ) + { + forget_base_memory ( phys_to_virt ( image_basemem ), + prefix_size_kb ); + /* Update image_basemem and image_basemem_size to + * indicate that our allocation now starts with _text + */ + image_basemem = runtime_start_kb; + image_basemem_size -= prefix_size_kb; + } +} + +/* Free base memory used by the runtime image. Called after + * relocation by arch_relocated_from(). + */ +void forget_runtime_base_memory ( unsigned long old_addr ) +{ + /* text_start_kb is old _text rounded down to a physical KB boundary */ + uint32_t old_text_start_kb = old_addr & ~0x3ff; + +#ifdef DEBUG_BASEMEM + printf ( "Attempting to free base memory used by runtime image\n" ); +#endif + + if ( ( image_basemem >= FREE_BASE_MEMORY ) && + ( image_basemem == old_text_start_kb ) ) + { + forget_base_memory ( phys_to_virt ( image_basemem ), + image_basemem_size ); + /* Update image_basemem to show no longer in use */ + image_basemem = 0; + image_basemem_size = 0; + } +} + +#endif /* PCBIOS */ diff --git a/src/arch/i386/firmware/pcbios/bios.c b/src/arch/i386/firmware/pcbios/bios.c new file mode 100644 index 00000000..70f26703 --- /dev/null +++ b/src/arch/i386/firmware/pcbios/bios.c @@ -0,0 +1,155 @@ +/* Etherboot routines for PCBIOS firmware. + * + * Body of routines taken from old pcbios.S + */ + +#ifdef PCBIOS + +#include "etherboot.h" +#include "realmode.h" +#include "segoff.h" + +#define CF ( 1 << 0 ) + +/************************************************************************** +CURRTICKS - Get Time +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. +**************************************************************************/ +#if defined(CONFIG_TSC_CURRTICKS) +#undef CONFIG_BIOS_CURRTICKS +#else +#define CONFIG_BIOS_CURRTICKS 1 +#endif +#if defined(CONFIG_BIOS_CURRTICKS) +unsigned long currticks (void) +{ + static uint32_t days = 0; + uint32_t *ticks = VIRTUAL(0x0040,0x006c); + uint8_t *midnight = VIRTUAL(0x0040,0x0070); + + /* Re-enable interrupts so that the timer interrupt can occur + */ + RM_FRAGMENT(rm_currticks, + "sti\n\t" + "nop\n\t" + "nop\n\t" + "cli\n\t" + ); + + real_call ( rm_currticks, NULL, NULL ); + + if ( *midnight ) { + *midnight = 0; + days += 0x1800b0; + } + return ( days + *ticks ); +} +#endif /* CONFIG_BIOS_CURRTICKS */ + +/************************************************************************** +INT15 - Call Interrupt 0x15 +**************************************************************************/ +int int15 ( int ax ) +{ + struct { + reg16_t ax; + } PACKED in_stack; + struct { + reg16_t flags; + } PACKED out_stack; + reg16_t ret_ax; + + RM_FRAGMENT(rm_int15, + "sti\n\t" + "popw %ax\n\t" + "stc\n\t" + "int $0x15\n\t" + "pushf\n\t" + "cli\n\t" + ); + + in_stack.ax.word = ax; + ret_ax.word = real_call ( rm_int15, &in_stack, &out_stack ); + + /* Carry flag clear indicates function not supported */ + if ( ! ( out_stack.flags.word & CF ) ) return 0; + return ret_ax.h; +} + +#ifdef POWERSAVE +/************************************************************************** +CPU_NAP - Save power by halting the CPU until the next interrupt +**************************************************************************/ +void cpu_nap ( void ) +{ + RM_FRAGMENT(rm_cpu_nap, + "sti\n\t" + "hlt\n\t" + "cli\n\t" + ); + + real_call ( rm_cpu_nap, NULL, NULL ); +} +#endif /* POWERSAVE */ + +#if (TRY_FLOPPY_FIRST > 0) +/************************************************************************** +DISK_INIT - Initialize the disk system +**************************************************************************/ +void disk_init ( void ) +{ + RM_FRAGMENT(rm_disk_init, + "sti\n\t" + "xorw %ax,%ax\n\t" + "movb $0x80,%dl\n\t" + "int $0x13\n\t" + "cli\n\t" + ); + + real_call ( rm_disk_init, NULL, NULL ); +} + +/************************************************************************** +DISK_READ - Read a sector from disk +**************************************************************************/ +unsigned int pcbios_disk_read ( int drive, int cylinder, int head, int sector, + char *buf ) { + struct { + reg16_t ax; + reg16_t cx; + reg16_t dx; + segoff_t buffer; + } PACKED in_stack; + struct { + reg16_t flags; + } PACKED out_stack; + reg16_t ret_ax; + + RM_FRAGMENT(rm_pcbios_disk_read, + "sti\n\t" + "popw %ax\n\t" + "popw %cx\n\t" + "popw %dx\n\t" + "popw %bx\n\t" + "popw %es\n\t" + "int $0x13\n\t" + "pushfw\n\t" + "cli\n\t" + ); + + in_stack.ax.h = 2; /* INT 13,2 - Read disk sector */ + in_stack.ax.l = 1; /* Read one sector */ + in_stack.cx.h = cylinder & 0xff; + in_stack.cx.l = ( ( cylinder >> 8 ) & 0x3 ) | sector; + in_stack.dx.h = head; + in_stack.dx.l = drive; + in_stack.buffer.segment = SEGMENT ( buf ); + in_stack.buffer.offset = OFFSET ( buf ); + ret_ax.word = real_call ( rm_pcbios_disk_read, &in_stack, &out_stack ); + return ( out_stack.flags.word & CF ) ? ret_ax.word : 0; +} +#endif /* TRY_FLOPPY_FIRST */ + +#endif /* PCBIOS */ diff --git a/src/arch/i386/firmware/pcbios/console.c b/src/arch/i386/firmware/pcbios/console.c new file mode 100644 index 00000000..f994f06e --- /dev/null +++ b/src/arch/i386/firmware/pcbios/console.c @@ -0,0 +1,85 @@ +/* Etherboot routines for PCBIOS firmware. + * + * Body of routines taken from old pcbios.S + */ + +#ifdef PCBIOS + +#include "etherboot.h" +#include "realmode.h" +#include "segoff.h" + +#define ZF ( 1 << 6 ) + +/************************************************************************** +CONSOLE_PUTC - Print a character on console +**************************************************************************/ +void console_putc ( int character ) +{ + struct { + reg16_t ax; + } PACKED in_stack; + + RM_FRAGMENT(rm_console_putc, + "sti\n\t" + "popw %ax\n\t" + "movb $0x0e, %ah\n\t" + "movl $1, %ebx\n\t" + "int $0x10\n\t" + "cli\n\t" + ); + + in_stack.ax.l = character; + real_call ( rm_console_putc, &in_stack, NULL ); +} + +/************************************************************************** +CONSOLE_GETC - Get a character from console +**************************************************************************/ +int console_getc ( void ) +{ + RM_FRAGMENT(rm_console_getc, + "sti\n\t" + "xorw %ax, %ax\n\t" + "int $0x16\n\t" + "xorb %ah, %ah\n\t" + "cli\n\t" + ); + + return real_call ( rm_console_getc, NULL, NULL ); +} + +/************************************************************************** +CONSOLE_ISCHAR - Check for keyboard interrupt +**************************************************************************/ +int console_ischar ( void ) +{ + RM_FRAGMENT(rm_console_ischar, + "sti\n\t" + "movb $1, %ah\n\t" + "int $0x16\n\t" + "pushfw\n\t" + "popw %ax\n\t" + "cli\n\t" + ); + + return ( ( real_call ( rm_console_ischar, NULL, NULL ) & ZF ) == 0 ); +} + +/************************************************************************** +GETSHIFT - Get keyboard shift state +**************************************************************************/ +int getshift ( void ) +{ + RM_FRAGMENT(rm_getshift, + "sti\n\t" + "movb $2, %ah\n\t" + "int $0x16\n\t" + "andw $0x3, %ax\n\t" + "cli\n\t" + ); + + return real_call ( rm_getshift, NULL, NULL ); +} + +#endif /* PCBIOS */ diff --git a/src/arch/i386/firmware/pcbios/e820mangler.S b/src/arch/i386/firmware/pcbios/e820mangler.S new file mode 100644 index 00000000..9349cf2b --- /dev/null +++ b/src/arch/i386/firmware/pcbios/e820mangler.S @@ -0,0 +1,296 @@ +#undef CODE16 +#if defined(PCBIOS) +#define CODE16 +#endif + +#ifdef CODE16 + +#define BOCHSBP xchgw %bx,%bx + + .text + .arch i386 + .section ".text16", "ax", @progbits + .code16 + +/**************************************************************************** + * Memory map mangling code + **************************************************************************** + */ + + .globl e820mangler +e820mangler: + +/* Macro to calculate offset of labels within code segment in + * installed copy of code. + */ +#define INSTALLED(x) ( (x) - e820mangler ) + +/**************************************************************************** + * Intercept INT 15 memory calls and remove the hidden memory ranges + * from the resulting memory map. + **************************************************************************** + */ + .globl _intercept_int15 +_intercept_int15: + /* Preserve registers */ + pushw %bp + /* Store %ax for future reference */ + pushw %ax + /* Make INT-style call to old INT15 routine */ + pushfw + lcall %cs:*INSTALLED(_intercepted_int15) + /* Preserve flags returned by original E820 routine */ + pushfw + /* Check for valid INT15 routine */ + jc intercept_int15_exit + /* Check for a routine we want to intercept */ + movw %sp, %bp + cmpw $0xe820, 2(%bp) + je intercept_e820 + cmpw $0xe801, 2(%bp) + je intercept_e801 + cmpb $0x88, 3(%bp) + je intercept_88 +intercept_int15_exit: + /* Restore registers and return */ + popfw + popw %bp /* discard original %ax */ + popw %bp + lret $2 /* 'iret' - flags already loaded */ + + .globl _intercepted_int15 +_intercepted_int15: .word 0,0 + +/**************************************************************************** + * Exclude an address range from a potentially overlapping address range + * + * Note: this *can* be called even if the range doesn't overlap; it + * will simply return the range unaltered. It copes with all the + * possible cases of overlap, including total overlap (which will + * modify the range to length zero). If the to-be-excluded range is + * in the middle of the target range, then the larger remaining + * portion will be returned. If %di is nonzero on entry then the + * range will only be truncated from the high end, i.e. the base + * address will never be altered. All this in less than 30 + * instructions. :) + * + * Parameters: + * %eax Base address of memory range + * %ecx Length of memory range + * %ebx Base address of memory range to exclude + * %edx Length of memory range to exclude + * %di 0 => truncate either end, 1 => truncate high end only + * Returns: + * %eax Updated base address of range + * %ecx Updated length of range + * %ebx,%edx Undefined + * All other registers (including %di) preserved + * + * Note: "ja" is used rather than "jg" because we are comparing + * unsigned ints + **************************************************************************** + */ +#ifdef TEST_EXCLUDE_ALGORITHM + .code32 +#endif /* TEST_EXCLUDE_ALGORITHM */ +exclude_memory_range: + /* Convert (start,length) to (start,end) */ + addl %eax, %ecx + addl %ebx, %edx + /* Calculate "prefix" length */ + subl %eax, %ebx /* %ebx = "prefix" length */ + ja 1f + xorl %ebx, %ebx /* Truncate to zero if negative */ +1: /* %di == 0 => truncate either end + * %di != 0 => truncate only high end + */ + testw %di, %di + je use_either + cmpl %eax, %edx + jbe 99f /* excl. range is below target range */ +use_prefix: /* Use prefix, discard suffix */ + addl %eax, %ebx /* %ebx = candidate end address */ + cmpl %ecx, %ebx /* %ecx = min ( %ebx, %ecx ) */ + ja 1f + movl %ebx, %ecx +1: jmp 99f +use_either: + /* Calculate "suffix" length */ + subl %ecx, %edx /* %edx = -( "suffix" length ) */ + jb 1f + xorl %edx, %edx /* Truncate to zero if negative */ +1: negl %edx /* %edx = "suffix" length */ + /* Use whichever is longest of "prefix" and "suffix" */ + cmpl %ebx, %edx + jbe use_prefix +use_suffix: /* Use suffix, discard prefix */ + negl %edx + addl %ecx, %edx /* %edx = candidate start address */ + cmpl %eax, %edx /* %eax = max ( %eax, %edx ) */ + jb 1f + movl %edx, %eax +1: +99: subl %eax, %ecx /* Convert back to (start,length) */ + ret + +#ifdef TEST_EXCLUDE_ALGORITHM + .globl __test_exclude +__test_exclude: + pushl %ebx + pushl %edi + movl 12(%esp), %eax + movl 16(%esp), %ecx + movl 20(%esp), %ebx + movl 24(%esp), %edx + movl 28(%esp), %edi + call exclude_memory_range + shll $16, %eax + orl %ecx, %eax + popl %edi + popl %ebx + ret + .code16 +#endif /* TEST_EXCLUDE_ALGORITHM */ + +/**************************************************************************** + * Exclude Etherboot-reserved address ranges from a potentially + * overlapping address range + * + * Parameters: + * %eax Base address of memory range + * %ecx Length of memory range + * %di 0 => truncate either end, 1 => truncate high end only + * Returns: + * %eax Updated base address of range + * %ecx Updated length of range + * All other registers (including %di) preserved + **************************************************************************** + */ +exclude_hidden_memory_ranges: + pushw %si + pushl %ebx + pushl %edx + movw $INSTALLED(_hide_memory), %si +2: movl %cs:0(%si), %ebx + movl %cs:4(%si), %edx + call exclude_memory_range + addw $8, %si + cmpw $INSTALLED(_hide_memory_end), %si + jl 2b + popl %edx + popl %ebx + popw %si + ret + + .globl _hide_memory +_hide_memory: + .long 0,0 /* Etherboot text (base,length) */ + .long 0,0 /* Heap (base,length) */ +_hide_memory_end: + +/**************************************************************************** + * Intercept INT 15,E820 calls and remove the hidden memory ranges + * from the resulting memory map. + **************************************************************************** + */ +#define SMAP ( 0x534d4150 ) +intercept_e820: + /* Check for valid E820 routine */ + cmpl $SMAP, %eax + jne intercept_int15_exit + /* If base address isn't in the low 4GB, return unaltered + * (since we never claim memory above 4GB). WARNING: we cheat + * by assuming that no E820 region will straddle the 4GB + * boundary: if this is not a valid assumption then things + * will probably break. + */ + cmpl $0, %es:4(%di) + jne intercept_int15_exit + /* Preserve registers */ + pushl %eax + pushl %ecx + /* Update returned memory range */ + movl %es:0(%di), %eax /* Base */ + movl %es:8(%di), %ecx /* Length */ + pushw %di + xorw %di, %di /* "truncate either end" flag */ + call exclude_hidden_memory_ranges + popw %di + movl %eax, %es:0(%di) /* Store updated base */ + movl %ecx, %es:8(%di) /* Store updated length */ + /* Restore registers and return */ + popl %ecx + popl %eax + jmp intercept_int15_exit + +/**************************************************************************** + * Intercept INT 15,E801 calls and remove the hidden memory ranges + * from the resulting memory map. + **************************************************************************** + */ +intercept_e801: + /* Adjust return values */ + call e801_adjust + xchgw %ax, %cx + xchgw %bx, %dx + call e801_adjust + xchgw %ax, %cx + xchgw %bx, %dx + jmp intercept_int15_exit + + /* %ax = #KB from 1MB+, %bx = #64KB from 16MB+ + * Return with modified values in %ax, %bx. Preserver other regs. + */ +e801_adjust: + pushw %di + pushl %ecx + pushl %eax + movw $1, %di /* "truncate only high end" flag */ + + /* Truncate #64KB from 16MB+ as appropriate */ + movw %bx, %cx /* (no need to zero high word) */ + shll $16, %ecx /* %ecx = length in bytes */ + movl $(1<<24), %eax /* 16MB start address */ + call exclude_hidden_memory_ranges + shrl $16, %ecx /* %cx = updated length in 64KB */ + movw %cx, %bx /* Return in %bx */ + + /* Truncate #KB from 1MB+ as appropriate */ + popw %cx /* Orig. %ax (high word already 0) */ + shll $10, %ecx /* %ecx = length in bytes */ + shrl $4, %eax /* 1MB start address */ + call exclude_hidden_memory_ranges + shrl $10, %ecx /* %cx = updated length in KB */ + pushw %cx /* Will be picked up in %eax */ + + popl %eax + popl %ecx + popw %di + ret + +/**************************************************************************** + * Intercept INT 15,88 calls and remove the hidden memory ranges + * from the resulting memory map. + **************************************************************************** + */ +intercept_88: + pushw %bx /* E801 adjust, ignore %bx */ + call e801_adjust + popw %bx + jmp intercept_int15_exit + + .globl e820mangler_end +e820mangler_end: + + .globl _e820mangler_size + .equ _e820mangler_size, e820mangler_end - e820mangler + .globl e820mangler_size +e820mangler_size: + .word _e820mangler_size + +#else + + .globl _e820mangler_size + .equ _e820mangler_size, 0 + +#endif /* CODE16 */ diff --git a/src/arch/i386/firmware/pcbios/hidemem.c b/src/arch/i386/firmware/pcbios/hidemem.c new file mode 100644 index 00000000..a9ae001e --- /dev/null +++ b/src/arch/i386/firmware/pcbios/hidemem.c @@ -0,0 +1,94 @@ +/* Utility functions to hide Etherboot by manipulating the E820 memory + * map. These could go in memsizes.c, but are placed here because not + * all images will need them. + */ + +#include "etherboot.h" +#include "hidemem.h" + +#ifdef CODE16 + +static int mangling = 0; +static void *mangler = NULL; + +#define INSTALLED(x) ( (typeof(&x)) ( (void*)(&x) - (void*)e820mangler \ + + mangler ) ) +#define intercept_int15 INSTALLED(_intercept_int15) +#define intercepted_int15 INSTALLED(_intercepted_int15) +#define hide_memory INSTALLED(_hide_memory) +#define INT15_VECTOR ( (segoff_t*) ( phys_to_virt( 4 * 0x15 ) ) ) + +int install_e820mangler ( void *new_mangler ) { + if ( mangling ) return 0; + memcpy ( new_mangler, &e820mangler, e820mangler_size ); + mangler = new_mangler; + return 1; +} + +/* Intercept INT15 calls and pass them through the mangler. The + * mangler must have been copied to base memory before making this + * call, and "mangler" must point to the base memory copy, which must + * be 16-byte aligned. + */ +int hide_etherboot ( void ) { + if ( mangling ) return 1; + if ( !mangler ) return 0; + + /* Hook INT15 handler */ + *intercepted_int15 = *INT15_VECTOR; + (*hide_memory)[0].start = virt_to_phys(_text); + (*hide_memory)[0].length = _end - _text; + /* IMPORTANT, possibly even FIXME: + * + * Etherboot has a tendency to claim a very large area of + * memory as possible heap; enough to make it impossible to + * load an OS if we hide all of it. We hide only the portion + * that's currently in use. This means that we MUST NOT + * perform further allocations from the heap while the mangler + * is active. + */ + (*hide_memory)[1].start = heap_ptr; + (*hide_memory)[1].length = heap_bot - heap_ptr; + INT15_VECTOR->segment = SEGMENT(mangler); + INT15_VECTOR->offset = 0; + + mangling = 1; + return 1; +} + +int unhide_etherboot ( void ) { + if ( !mangling ) return 1; + + /* Restore original INT15 handler + */ + if ( VIRTUAL(INT15_VECTOR->segment,INT15_VECTOR->offset) != mangler ) { + /* Oh dear... */ + +#ifdef WORK_AROUND_BPBATCH_BUG + /* BpBatch intercepts INT15, so can't unhook it, and + * then proceeds to ignore our PXENV_KEEP_UNDI return + * status, which means that it ends up zeroing out the + * INT15 handler routine. + * + * This rather ugly hack involves poking into + * BpBatch's code and changing it's stored value for + * the "next handler" in the INT15 chain. + */ + segoff_t *bp_chain = VIRTUAL ( 0x0060, 0x8254 ); + + if ( ( bp_chain->segment == SEGMENT(mangler) ) && + ( bp_chain->offset == 0 ) ) { + printf ( "\nBPBATCH bug workaround enabled\n" ); + *bp_chain = *intercepted_int15; + } +#endif /* WORK_AROUND_BPBATCH_BUG */ + + return 0; + } + *INT15_VECTOR = *intercepted_int15; + + mangling = 0; + return 1; +} + +#endif /* CODE16 */ diff --git a/src/arch/i386/firmware/pcbios/memsizes.c b/src/arch/i386/firmware/pcbios/memsizes.c new file mode 100644 index 00000000..18ae86da --- /dev/null +++ b/src/arch/i386/firmware/pcbios/memsizes.c @@ -0,0 +1,201 @@ +#ifdef PCBIOS + +#include "etherboot.h" +#include "realmode.h" + +#define CF ( 1 << 0 ) + +#ifndef MEMSIZES_DEBUG +#define MEMSIZES_DEBUG 0 +#endif + +/* by Eric Biederman */ + +struct meminfo meminfo; + +/************************************************************************** +BASEMEMSIZE - Get size of the conventional (base) memory +**************************************************************************/ +unsigned short basememsize ( void ) +{ + RM_FRAGMENT(rm_basememsize, + "int $0x12\n\t" + ); + return real_call ( rm_basememsize, NULL, NULL ); +} + +/************************************************************************** +MEMSIZE - Determine size of extended memory +**************************************************************************/ +unsigned int memsize ( void ) +{ + struct { + reg16_t ax; + } PACKED in_stack; + struct { + reg16_t ax; + reg16_t bx; + reg16_t cx; + reg16_t dx; + reg16_t flags; + } PACKED out_stack; + int memsize; + + RM_FRAGMENT(rm_memsize, + /* Some buggy BIOSes don't clear/set carry on pass/error of + * e801h memory size call or merely pass cx,dx through without + * changing them, so we set carry and zero cx,dx before call. + */ + "stc\n\t" + "xorw %cx,%cx\n\t" + "xorw %dx,%dx\n\t" + "popw %ax\n\t" + "int $0x15\n\t" + "pushfw\n\t" + "pushw %dx\n\t" + "pushw %cx\n\t" + "pushw %bx\n\t" + "pushw %ax\n\t" + ); + + /* Try INT 15,e801 first */ + in_stack.ax.word = 0xe801; + real_call ( rm_memsize, &in_stack, &out_stack ); + if ( out_stack.flags.word & CF ) { + /* INT 15,e801 not supported: try INT 15,88 */ + in_stack.ax.word = 0x8800; + memsize = real_call ( rm_memsize, &in_stack, &out_stack ); + } else { + /* Some BIOSes report extended memory via ax,bx rather + * than cx,dx + */ + if ( (out_stack.cx.word==0) && (out_stack.dx.word==0) ) { + /* Use ax,bx */ + memsize = ( out_stack.bx.word<<6 ) + out_stack.ax.word; + } else { + /* Use cx,dx */ + memsize = ( out_stack.dx.word<<6 ) + out_stack.cx.word; + } + } + return memsize; +} + +#define SMAP ( 0x534d4150 ) +int meme820 ( struct e820entry *buf, int count ) +{ + struct { + reg16_t flags; + reg32_t eax; + reg32_t ebx; + struct e820entry entry; + } PACKED stack; + int index = 0; + + RM_FRAGMENT(rm_meme820, + "addw $6, %sp\n\t" /* skip flags, eax */ + "popl %ebx\n\t" + "pushw %ss\n\t" /* es:di = ss:sp */ + "popw %es\n\t" + "movw %sp, %di\n\t" + "movl $0xe820, %eax\n\t" + "movl $" RM_STR(SMAP) ", %edx\n\t" + "movl $" RM_STR(E820ENTRY_SIZE) ", %ecx\n\t" + "int $0x15\n\t" + "pushl %ebx\n\t" + "pushl %eax\n\t" + "pushfw\n\t" + ); + + stack.ebx.dword = 0; /* 'EOF' marker */ + while ( ( index < count ) && + ( ( index == 0 ) || ( stack.ebx.dword != 0 ) ) ) { + real_call ( rm_meme820, &stack, &stack ); + if ( stack.eax.dword != SMAP ) return 0; + if ( stack.flags.word & CF ) return 0; + buf[index++] = stack.entry; + } + return index; +} + +void get_memsizes(void) +{ + /* Ensure we don't stomp bios data structutres. + * the interrupt table: 0x000 - 0x3ff + * the bios data area: 0x400 - 0x502 + * Dos variables: 0x502 - 0x5ff + */ + static const unsigned min_addr = 0x600; + unsigned i; + unsigned basemem; + basemem = get_free_base_memory(); + meminfo.basememsize = basememsize(); + meminfo.memsize = memsize(); +#ifndef IGNORE_E820_MAP + meminfo.map_count = meme820(meminfo.map, E820MAX); +#else + meminfo.map_count = 0; +#endif + if (meminfo.map_count == 0) { + /* If we don't have an e820 memory map fake it */ + meminfo.map_count = 2; + meminfo.map[0].addr = 0; + meminfo.map[0].size = meminfo.basememsize << 10; + meminfo.map[0].type = E820_RAM; + meminfo.map[1].addr = 1024*1024; + meminfo.map[1].size = meminfo.memsize << 10; + meminfo.map[1].type = E820_RAM; + } + /* Scrub the e820 map */ + for(i = 0; i < meminfo.map_count; i++) { + if (meminfo.map[i].type != E820_RAM) { + continue; + } + /* Reserve the bios data structures */ + if (meminfo.map[i].addr < min_addr) { + unsigned long delta; + delta = min_addr - meminfo.map[i].addr; + if (delta > meminfo.map[i].size) { + delta = meminfo.map[i].size; + } + meminfo.map[i].addr = min_addr; + meminfo.map[i].size -= delta; + } + /* Ensure the returned e820 map is in sync + * with the actual memory state + */ + if ((meminfo.map[i].addr < 0xa0000) && + ((meminfo.map[i].addr + meminfo.map[i].size) > basemem)) + { + if (meminfo.map[i].addr <= basemem) { + meminfo.map[i].size = basemem - meminfo.map[i].addr; + } else { + meminfo.map[i].addr = basemem; + meminfo.map[i].size = 0; + } + } + } +#if MEMSIZES_DEBUG +{ + int i; + printf("basememsize %d\n", meminfo.basememsize); + printf("memsize %d\n", meminfo.memsize); + printf("Memory regions(%d):\n", meminfo.map_count); + for(i = 0; i < meminfo.map_count; i++) { + unsigned long long r_start, r_end; + r_start = meminfo.map[i].addr; + r_end = r_start + meminfo.map[i].size; + printf("[%X%X, %X%X) type %d\n", + (unsigned long)(r_start >> 32), + (unsigned long)r_start, + (unsigned long)(r_end >> 32), + (unsigned long)r_end, + meminfo.map[i].type); +#if defined(CONSOLE_FIRMWARE) + sleep(1); /* No way to see 32 entries on a standard 80x25 screen... */ +#endif + } +} +#endif +} + +#endif /* PCBIOS */ diff --git a/src/arch/i386/include/bits/byteswap.h b/src/arch/i386/include/bits/byteswap.h new file mode 100644 index 00000000..1bc84fda --- /dev/null +++ b/src/arch/i386/include/bits/byteswap.h @@ -0,0 +1,45 @@ +#ifndef ETHERBOOT_BITS_BYTESWAP_H +#define ETHERBOOT_BITS_BYTESWAP_H + +static inline uint16_t __i386_bswap_16(uint16_t x) +{ + __asm__("xchgb %b0,%h0\n\t" + : "=q" (x) + : "0" (x)); + return x; +} + +static inline 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; +} + + +#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_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))) + + +#endif /* ETHERBOOT_BITS_BYTESWAP_H */ diff --git a/src/arch/i386/include/bits/cpu.h b/src/arch/i386/include/bits/cpu.h new file mode 100644 index 00000000..2e9b27ba --- /dev/null +++ b/src/arch/i386/include/bits/cpu.h @@ -0,0 +1,243 @@ +#ifndef I386_BITS_CPU_H +#define I386_BITS_CPU_H + + +/* Sample usage: CPU_FEATURE_P(cpu.x86_capability, FPU) */ +#define CPU_FEATURE_P(CAP, FEATURE) \ + (!!(CAP[(X86_FEATURE_##FEATURE)/32] & ((X86_FEATURE_##FEATURE) & 0x1f))) + +#define NCAPINTS 4 /* Currently we have 4 32-bit words worth of info */ + +/* Intel-defined CPU features, CPUID level 0x00000001, word 0 */ +#define X86_FEATURE_FPU (0*32+ 0) /* Onboard FPU */ +#define X86_FEATURE_VME (0*32+ 1) /* Virtual Mode Extensions */ +#define X86_FEATURE_DE (0*32+ 2) /* Debugging Extensions */ +#define X86_FEATURE_PSE (0*32+ 3) /* Page Size Extensions */ +#define X86_FEATURE_TSC (0*32+ 4) /* Time Stamp Counter */ +#define X86_FEATURE_MSR (0*32+ 5) /* Model-Specific Registers, RDMSR, WRMSR */ +#define X86_FEATURE_PAE (0*32+ 6) /* Physical Address Extensions */ +#define X86_FEATURE_MCE (0*32+ 7) /* Machine Check Architecture */ +#define X86_FEATURE_CX8 (0*32+ 8) /* CMPXCHG8 instruction */ +#define X86_FEATURE_APIC (0*32+ 9) /* Onboard APIC */ +#define X86_FEATURE_SEP (0*32+11) /* SYSENTER/SYSEXIT */ +#define X86_FEATURE_MTRR (0*32+12) /* Memory Type Range Registers */ +#define X86_FEATURE_PGE (0*32+13) /* Page Global Enable */ +#define X86_FEATURE_MCA (0*32+14) /* Machine Check Architecture */ +#define X86_FEATURE_CMOV (0*32+15) /* CMOV instruction (FCMOVCC and FCOMI too if FPU present) */ +#define X86_FEATURE_PAT (0*32+16) /* Page Attribute Table */ +#define X86_FEATURE_PSE36 (0*32+17) /* 36-bit PSEs */ +#define X86_FEATURE_PN (0*32+18) /* Processor serial number */ +#define X86_FEATURE_CLFLSH (0*32+19) /* Supports the CLFLUSH instruction */ +#define X86_FEATURE_DTES (0*32+21) /* Debug Trace Store */ +#define X86_FEATURE_ACPI (0*32+22) /* ACPI via MSR */ +#define X86_FEATURE_MMX (0*32+23) /* Multimedia Extensions */ +#define X86_FEATURE_FXSR (0*32+24) /* FXSAVE and FXRSTOR instructions (fast save and restore */ + /* of FPU context), and CR4.OSFXSR available */ +#define X86_FEATURE_XMM (0*32+25) /* Streaming SIMD Extensions */ +#define X86_FEATURE_XMM2 (0*32+26) /* Streaming SIMD Extensions-2 */ +#define X86_FEATURE_SELFSNOOP (0*32+27) /* CPU self snoop */ +#define X86_FEATURE_HT (0*32+28) /* Hyper-Threading */ +#define X86_FEATURE_ACC (0*32+29) /* Automatic clock control */ +#define X86_FEATURE_IA64 (0*32+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 (1*32+11) /* SYSCALL/SYSRET */ +#define X86_FEATURE_MMXEXT (1*32+22) /* AMD MMX extensions */ +#define X86_FEATURE_LM (1*32+29) /* Long Mode (x86-64) */ +#define X86_FEATURE_3DNOWEXT (1*32+30) /* AMD 3DNow! extensions */ +#define X86_FEATURE_3DNOW (1*32+31) /* 3DNow! */ + +/* Transmeta-defined CPU features, CPUID level 0x80860001, word 2 */ +#define X86_FEATURE_RECOVERY (2*32+ 0) /* CPU in recovery mode */ +#define X86_FEATURE_LONGRUN (2*32+ 1) /* Longrun power control */ +#define X86_FEATURE_LRTI (2*32+ 3) /* LongRun table interface */ + +/* Other features, Linux-defined mapping, word 3 */ +/* This range is used for feature bits which conflict or are synthesized */ +#define X86_FEATURE_CXMMX (3*32+ 0) /* Cyrix MMX extensions */ +#define X86_FEATURE_K6_MTRR (3*32+ 1) /* AMD K6 nonstandard MTRRs */ +#define X86_FEATURE_CYRIX_ARR (3*32+ 2) /* Cyrix ARRs (= MTRRs) */ +#define X86_FEATURE_CENTAUR_MCR (3*32+ 3) /* Centaur MCRs (= MTRRs) */ + +#define MAX_X86_VENDOR_ID 16 +struct cpuinfo_x86 { + uint8_t x86; /* CPU family */ + uint8_t x86_model; + uint8_t x86_mask; + + int cpuid_level; /* Maximum supported CPUID level, -1=no CPUID */ + unsigned x86_capability[NCAPINTS]; + char x86_vendor_id[MAX_X86_VENDOR_ID]; +}; + + +#define X86_VENDOR_INTEL 0 +#define X86_VENDOR_CYRIX 1 +#define X86_VENDOR_AMD 2 +#define X86_VENDOR_UMC 3 +#define X86_VENDOR_NEXGEN 4 +#define X86_VENDOR_CENTAUR 5 +#define X86_VENDOR_RISE 6 +#define X86_VENDOR_TRANSMETA 7 +#define X86_VENDOR_NSC 8 +#define X86_VENDOR_UNKNOWN 0xff + +/* + * 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 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)); +} + +/* + * CPUID functions returning a single datum + */ +static inline unsigned int cpuid_eax(unsigned int op) +{ + unsigned int eax; + + __asm__("cpuid" + : "=a" (eax) + : "0" (op) + : "bx", "cx", "dx"); + return eax; +} +static inline unsigned int cpuid_ebx(unsigned int op) +{ + unsigned int eax, ebx; + + __asm__("cpuid" + : "=a" (eax), "=b" (ebx) + : "0" (op) + : "cx", "dx" ); + return ebx; +} +static inline unsigned int cpuid_ecx(unsigned int op) +{ + unsigned int eax, ecx; + + __asm__("cpuid" + : "=a" (eax), "=c" (ecx) + : "0" (op) + : "bx", "dx" ); + return ecx; +} +static inline unsigned int cpuid_edx(unsigned int op) +{ + unsigned int eax, edx; + + __asm__("cpuid" + : "=a" (eax), "=d" (edx) + : "0" (op) + : "bx", "cx"); + return edx; +} + +/* + * Intel CPU features in CR4 + */ +#define X86_CR4_VME 0x0001 /* enable vm86 extensions */ +#define X86_CR4_PVI 0x0002 /* virtual interrupts flag enable */ +#define X86_CR4_TSD 0x0004 /* disable time stamp at ipl 3 */ +#define X86_CR4_DE 0x0008 /* enable debugging extensions */ +#define X86_CR4_PSE 0x0010 /* enable page size extensions */ +#define X86_CR4_PAE 0x0020 /* enable physical address extensions */ +#define X86_CR4_MCE 0x0040 /* Machine check enable */ +#define X86_CR4_PGE 0x0080 /* enable global pages */ +#define X86_CR4_PCE 0x0100 /* enable performance counters at ipl 3 */ +#define X86_CR4_OSFXSR 0x0200 /* enable fast FPU save and restore */ +#define X86_CR4_OSXMMEXCPT 0x0400 /* enable unmasked SSE exceptions */ + + +#define MSR_K6_EFER 0xC0000080 +/* EFER bits: */ +#define _EFER_SCE 0 /* SYSCALL/SYSRET */ +#define _EFER_LME 8 /* Long mode enable */ +#define _EFER_LMA 10 /* Long mode active (read-only) */ +#define _EFER_NX 11 /* No execute enable */ + +#define EFER_SCE (1<<_EFER_SCE) +#define EFER_LME (1<, 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/src/arch/i386/include/hidemem.h b/src/arch/i386/include/hidemem.h new file mode 100644 index 00000000..600a8692 --- /dev/null +++ b/src/arch/i386/include/hidemem.h @@ -0,0 +1,21 @@ +#ifndef HIDEMEM_H +#define HIDEMEM_H + +#include "segoff.h" + +extern int install_e820mangler ( void *new_mangler ); +extern int hide_etherboot ( void ); +extern int unhide_etherboot ( void ); + +/* Symbols in e820mangler.S */ +extern void e820mangler ( void ); +extern void _intercept_int15 ( void ); +extern segoff_t _intercepted_int15; +typedef struct { + uint32_t start; + uint32_t length; +} exclude_range_t; +extern exclude_range_t _hide_memory[2]; +extern uint16_t e820mangler_size; + +#endif /* HIDEMEM_H */ diff --git a/src/arch/i386/include/hooks.h b/src/arch/i386/include/hooks.h new file mode 100644 index 00000000..0764edaf --- /dev/null +++ b/src/arch/i386/include/hooks.h @@ -0,0 +1,9 @@ +#ifndef ETHERBOOT_I386_HOOKS_H +#define ETHERBOOT_I386_HOOKS_H + +void arch_main(in_call_data_t *data, va_list params); +void arch_on_exit(int status); +#define arch_relocate_to(addr) +void arch_relocated_from ( uint32_t old_addr ); + +#endif /* ETHERBOOT_I386_HOOKS_H */ diff --git a/src/arch/i386/include/io.h b/src/arch/i386/include/io.h new file mode 100644 index 00000000..e351a0c1 --- /dev/null +++ b/src/arch/i386/include/io.h @@ -0,0 +1,246 @@ +#ifndef ETHERBOOT_IO_H +#define ETHERBOOT_IO_H + + +/* Amount of relocation etherboot is experiencing */ +extern unsigned long virt_offset; + +/* Don't require identity mapped physical memory, + * osloader.c is the only valid user at the moment. + */ +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); +} + +/* 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. + */ +#define readb(addr) (*(volatile unsigned char *) (addr)) +#define readw(addr) (*(volatile unsigned short *) (addr)) +#define readl(addr) (*(volatile unsigned int *) (addr)) + +#define writeb(b,addr) ((*(volatile unsigned char *) (addr)) = (b)) +#define writew(b,addr) ((*(volatile unsigned short *) (addr)) = (b)) +#define writel(b,addr) ((*(volatile unsigned int *) (addr)) = (b)) + +#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/src/arch/i386/include/latch.h b/src/arch/i386/include/latch.h new file mode 100644 index 00000000..38a8bd21 --- /dev/null +++ b/src/arch/i386/include/latch.h @@ -0,0 +1,10 @@ +#ifndef LATCH_H +#define LATCH_H + +#define TICKS_PER_SEC 18 + +/* For different calibrators of the TSC move the declaration of + * sleep_latch and the definitions of it's length here... + */ + +#endif /* LATCH_H */ diff --git a/src/arch/i386/include/limits.h b/src/arch/i386/include/limits.h new file mode 100644 index 00000000..f13db267 --- /dev/null +++ b/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/src/arch/i386/include/pic8259.h b/src/arch/i386/include/pic8259.h new file mode 100644 index 00000000..694e9d13 --- /dev/null +++ b/src/arch/i386/include/pic8259.h @@ -0,0 +1,96 @@ +/* + * 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 "segoff.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(x) ( (x) < IRQ_PIC_CUTOFF ? PIC1_ICR : PIC2_ICR ) +#define ICR_VALUE(x) ( (x) % IRQ_PIC_CUTOFF ) +#define CHAINED_IRQ 2 + +/* Utility macros to convert IRQ numbers to INT numbers and INT vectors */ +#define IRQ_INT(x) ( (x) +#include +#include + +/* Segment:offset structure. Note that the order within the structure + * is offset:segment. + */ +typedef struct { + uint16_t offset; + uint16_t segment; +} segoff_t; + +/* Macros for converting from virtual to segment:offset addresses, + * when we don't actually care which of the many isomorphic results we + * get. + */ +#ifdef DEBUG_SEGMENT +uint16_t SEGMENT ( const void * const ptr ) { + uint32_t phys = virt_to_phys ( ptr ); + if ( phys > 0xfffff ) { + printf ( "FATAL ERROR: segment address out of range\n" ); + } + return phys >> 4; +} +#else +#define SEGMENT(x) ( virt_to_phys ( x ) >> 4 ) +#endif +#define OFFSET(x) ( virt_to_phys ( x ) & 0xf ) +#define SEGOFF(x) { OFFSET(x), SEGMENT(x) } +#define VIRTUAL(x,y) ( phys_to_virt ( ( ( x ) << 4 ) + ( y ) ) ) + +#endif /* SEGOFF_H */ diff --git a/src/arch/i386/include/setjmp.h b/src/arch/i386/include/setjmp.h new file mode 100644 index 00000000..ed2be270 --- /dev/null +++ b/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/src/arch/i386/include/stdint.h b/src/arch/i386/include/stdint.h new file mode 100644 index 00000000..42b04830 --- /dev/null +++ b/src/arch/i386/include/stdint.h @@ -0,0 +1,16 @@ +#ifndef STDINT_H +#define STDINT_H + +typedef unsigned size_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; + +#endif /* STDINT_H */ diff --git a/src/arch/i386/include/vga.h b/src/arch/i386/include/vga.h new file mode 100644 index 00000000..01fc39d8 --- /dev/null +++ b/src/arch/i386/include/vga.h @@ -0,0 +1,228 @@ +/* + * + * modified + * by Steve M. Gehlbach + * + * Originally from linux/drivers/video/vga16.c by + * Ben Pfaff and Petr Vandrovec + * Copyright 1999 Ben Pfaff and Petr Vandrovec + * Based on VGA info at http://www.goodnet.com/~tinara/FreeVGA/home.htm + * Based on VESA framebuffer (c) 1998 Gerd Knorr + * + */ + +#ifndef VGA_H_INCL +#define VGA_H_INCL 1 + +//#include + +#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/src/arch/i386/prefix/bImageprefix.S b/src/arch/i386/prefix/bImageprefix.S new file mode 100644 index 00000000..a478713d --- /dev/null +++ b/src/arch/i386/prefix/bImageprefix.S @@ -0,0 +1,614 @@ +/* + 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 + .globl _prefix +_prefix: + +/* + 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, $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 + .globl prefix_exit +prefix_exit: + int $0x19 /* should try to boot machine */ + .globl prefix_exit_end +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/src/arch/i386/prefix/boot1a.s b/src/arch/i386/prefix/boot1a.s new file mode 100644 index 00000000..557462f1 --- /dev/null +++ b/src/arch/i386/prefix/boot1a.s @@ -0,0 +1,410 @@ +# This code is no longer used in Etherboot. It is not maintained and +# may not work. + + +# +# Copyright (c) 1998 Robert Nordier +# All rights reserved. +# Very small bootrom changes by Luigi Rizzo +# +# I recently had the problem of downloading the etherboot code +# from a hard disk partition instead of a floppy, and noticed that +# floppyload.S does not do the job. With a bit of hacking to +# the FreeBSD's boot1.s code, I managed to obtain a boot sector +# which works both for floppies and hard disks -- basically you +# do something like +# +# cat boot1a bin32/.lzrom > /dev/ad0s4 +# +# (or whatever is the HD partition you are using, I am using slice +# 4 on FreeBSD) and you are up and running. +# Then with "fdisk" you have to mark your partition as having type "1" +# (which is listed as DOS-- but basically it must be something matching +# the variable PRT_BSD in the assembly source below). +# +# +# Redistribution and use in source and binary forms are freely +# permitted provided that the above copyright notice and this +# paragraph and the following disclaimer are duplicated in all +# such forms. +# +# This software is provided "AS IS" and without any express or +# implied warranties, including, without limitation, the implied +# warranties of merchantability and fitness for a particular +# purpose. +# +# Makefile: +#boot1a: boot1a.out +# objcopy -S -O binary boot1a.out boot1a +# +#boot1a.out: boot1a.o +# ld -nostdlib -static -N -e start -Ttext 0x7c00 -o boot1a.out boot1a.o +# +#boot1a.o: boot1a.s +# as --defsym FLAGS=0x80 boot1a.s -o boot1a.o +# +# + +# $FreeBSD: src/sys/boot/i386/boot2/boot1.s,v 1.10.2.2 2000/07/07 21:12:32 jhb Exp $ + +# Memory Locations + .set MEM_REL,0x700 # Relocation address + .set MEM_ARG,0x900 # Arguments + .set MEM_ORG,0x7c00 # Origin + .set MEM_BUF,0x8c00 # Load area + .set MEM_BTX,0x9000 # BTX start + .set MEM_JMP,0x9010 # BTX entry point + .set MEM_USR,0xa000 # Client start + .set BDA_BOOT,0x472 # Boot howto flag + +# Partition Constants + .set PRT_OFF,0x1be # Partition offset + .set PRT_NUM,0x4 # Partitions + .set PRT_BSD,0x1 # Partition type + +# Flag Bits + .set FL_PACKET,0x80 # Packet mode + +# Misc. Constants + .set SIZ_PAG,0x1000 # Page size + .set SIZ_SEC,0x200 # Sector size + + .globl start + .globl xread + .code16 + +start: jmp main # Start recognizably + + .org 0x4,0x90 +# +# Trampoline used by boot2 to call read to read data from the disk via +# the BIOS. Call with: +# +# %cx:%ax - long - LBA to read in +# %es:(%bx) - caddr_t - buffer to read data into +# %dl - byte - drive to read from +# %dh - byte - num sectors to read +# + +xread: push %ss # Address + pop %ds # data +# +# Setup an EDD disk packet and pass it to read +# +xread.1: # Starting + pushl $0x0 # absolute + push %cx # block + push %ax # number + push %es # Address of + push %bx # transfer buffer + xor %ax,%ax # Number of + movb %dh,%al # blocks to + push %ax # transfer + push $0x10 # Size of packet + mov %sp,%bp # Packet pointer + callw read # Read from disk + lea 0x10(%bp),%sp # Clear stack + lret # To far caller +# +# Load the rest of boot2 and BTX up, copy the parts to the right locations, +# and start it all up. +# + +# +# Setup the segment registers to flat addressing (segment 0) and setup the +# stack to end just below the start of our code. +# +main: cld # String ops inc + xor %cx,%cx # Zero + mov %cx,%es # Address + mov %cx,%ds # data + mov %cx,%ss # Set up + mov $start,%sp # stack +# +# Relocate ourself to MEM_REL. Since %cx == 0, the inc %ch sets +# %cx == 0x100. +# + mov %sp,%si # Source + mov $MEM_REL,%di # Destination + incb %ch # Word count + rep # Copy + movsw # code +# +# If we are on a hard drive, then load the MBR and look for the first +# FreeBSD slice. We use the fake partition entry below that points to +# the MBR when we call nread. The first pass looks for the first active +# FreeBSD slice. The second pass looks for the first non-active FreeBSD +# slice if the first one fails. +# + mov $part4,%si # Partition + cmpb $0x80,%dl # Hard drive? + jb main.4 # No + movb $0x1,%dh # Block count + callw nread # Read MBR + mov $0x1,%cx # Two passes +main.1: mov $MEM_BUF+PRT_OFF,%si # Partition table + movb $0x1,%dh # Partition +main.2: cmpb $PRT_BSD,0x4(%si) # Our partition type? + jne main.3 # No + jcxz main.5 # If second pass + testb $0x80,(%si) # Active? + jnz main.5 # Yes +main.3: add $0x10,%si # Next entry + incb %dh # Partition + cmpb $0x1+PRT_NUM,%dh # In table? + jb main.2 # Yes + dec %cx # Do two + jcxz main.1 # passes +# +# If we get here, we didn't find any FreeBSD slices at all, so print an +# error message and die. +# +booterror: mov $msg_part,%si # Message + jmp error # Error +# +# Floppies use partition 0 of drive 0. +# +main.4: xor %dx,%dx # Partition:drive +# +# Ok, we have a slice and drive in %dx now, so use that to locate and load +# boot2. %si references the start of the slice we are looking for, so go +# ahead and load up the first 16 sectors (boot1 + boot2) from that. When +# we read it in, we conveniently use 0x8c00 as our transfer buffer. Thus, +# boot1 ends up at 0x8c00, and boot2 starts at 0x8c00 + 0x200 = 0x8e00. +# The first part of boot2 is the disklabel, which is 0x200 bytes long. +# The second part is BTX, which is thus loaded into 0x9000, which is where +# it also runs from. The boot2.bin binary starts right after the end of +# BTX, so we have to figure out where the start of it is and then move the +# binary to 0xb000. Normally, BTX clients start at MEM_USR, or 0xa000, but +# when we use btxld create boot2, we use an entry point of 0x1000. That +# entry point is relative to MEM_USR; thus boot2.bin starts at 0xb000. +# +main.5: mov %dx,MEM_ARG # Save args + movb $0x2,%dh # Sector count + mov $0x7e00, %bx + callw nreadbx # Read disk + movb $0x40,%dh # Sector count + movb %dh, %al + callw puthex + mov $0x7e00, %bx + callw nreadbx # Read disk + push %si + mov $msg_r1,%si + callw putstr + pop %si + lcall $0x800,$0 # enter the rom code + int $0x19 + +msg_r1: .asciz " done\r\n" + +.if 0 + mov $MEM_BTX,%bx # BTX + mov 0xa(%bx),%si # Get BTX length and set + add %bx,%si # %si to start of boot2.bin + mov $MEM_USR+SIZ_PAG,%di # Client page 1 + mov $MEM_BTX+0xe*SIZ_SEC,%cx # Byte + sub %si,%cx # count + rep # Relocate + movsb # client + sub %di,%cx # Byte count + xorb %al,%al # Zero assumed bss from + rep # the end of boot2.bin + stosb # up to 0x10000 + callw seta20 # Enable A20 + jmp start+MEM_JMP-MEM_ORG # Start BTX +# +# Enable A20 so we can access memory above 1 meg. +# +seta20: cli # Disable interrupts +seta20.1: inb $0x64,%al # Get status + testb $0x2,%al # Busy? + jnz seta20.1 # Yes + movb $0xd1,%al # Command: Write + outb %al,$0x64 # output port +seta20.2: inb $0x64,%al # Get status + testb $0x2,%al # Busy? + jnz seta20.2 # Yes + movb $0xdf,%al # Enable + outb %al,$0x60 # A20 + sti # Enable interrupts + retw # To caller +.endif +# +# Trampoline used to call read from within boot1. +# +nread: mov $MEM_BUF,%bx # Transfer buffer +nreadbx: # same but address is in bx + mov 0x8(%si),%ax # Get + mov 0xa(%si),%cx # LBA + push %bx + push %ax + callw putword + pop %ax + pop %bx + push %cs # Read from + callw xread.1 # disk + jnc return # If success, return + mov $msg_read,%si # Otherwise, set the error + # message and fall through to + # the error routine +# +# Print out the error message pointed to by %ds:(%si) followed +# by a prompt, wait for a keypress, and then reboot the machine. +# +error: callw putstr # Display message + mov $prompt,%si # Display + callw putstr # prompt + xorb %ah,%ah # BIOS: Get + int $0x16 # keypress + movw $0x1234, BDA_BOOT # Do a warm boot + ljmp $0xffff,$0x0 # reboot the machine +# +# Display a null-terminated string using the BIOS output. +# +putstr.0: call putchar +putstr: lodsb # Get char + testb %al,%al # End of string? + jne putstr.0 # No + retw + +putword: push %ax + movb $'.', %al + callw putchar + movb %ah, %al + callw puthex + pop %ax +puthex: push %ax + shr $4, %al + callw putdigit + pop %ax +putdigit: + andb $0xf, %al + addb $0x30, %al + cmpb $0x39, %al + jbe putchar + addb $7, %al +putchar: push %ax + mov $0x7,%bx + movb $0xe,%ah + int $0x10 + pop %ax + retw + +# +# Overused return code. ereturn is used to return an error from the +# read function. Since we assume putstr succeeds, we (ab)use the +# same code when we return from putstr. +# +ereturn: movb $0x1,%ah # Invalid + stc # argument +return: retw # To caller +# +# Reads sectors from the disk. If EDD is enabled, then check if it is +# installed and use it if it is. If it is not installed or not enabled, then +# fall back to using CHS. Since we use a LBA, if we are using CHS, we have to +# fetch the drive parameters from the BIOS and divide it out ourselves. +# Call with: +# +# %dl - byte - drive number +# stack - 10 bytes - EDD Packet +# +read: push %dx # Save + movb $0x8,%ah # BIOS: Get drive + int $0x13 # parameters + movb %dh,%ch # Max head number + pop %dx # Restore + jc return # If error + andb $0x3f,%cl # Sectors per track + jz ereturn # If zero + cli # Disable interrupts + mov 0x8(%bp),%eax # Get LBA + push %dx # Save + movzbl %cl,%ebx # Divide by + xor %edx,%edx # sectors + div %ebx # per track + movb %ch,%bl # Max head number + movb %dl,%ch # Sector number + inc %bx # Divide by + xorb %dl,%dl # number + div %ebx # of heads + movb %dl,%bh # Head number + pop %dx # Restore + cmpl $0x3ff,%eax # Cylinder number supportable? + sti # Enable interrupts + ja read.7 # No, try EDD + xchgb %al,%ah # Set up cylinder + rorb $0x2,%al # number + orb %ch,%al # Merge + inc %ax # sector + xchg %ax,%cx # number + movb %bh,%dh # Head number + subb %ah,%al # Sectors this track + mov 0x2(%bp),%ah # Blocks to read + cmpb %ah,%al # To read + jb read.2 # this + movb %ah,%al # track +read.2: mov $0x5,%di # Try count +read.3: les 0x4(%bp),%bx # Transfer buffer + push %ax # Save + movb $0x2,%ah # BIOS: Read + int $0x13 # from disk + pop %bx # Restore + jnc read.4 # If success + dec %di # Retry? + jz read.6 # No + xorb %ah,%ah # BIOS: Reset + int $0x13 # disk system + xchg %bx,%ax # Block count + jmp read.3 # Continue +read.4: movzbw %bl,%ax # Sectors read + add %ax,0x8(%bp) # Adjust + jnc read.5 # LBA, + incw 0xa(%bp) # transfer +read.5: shlb %bl # buffer + add %bl,0x5(%bp) # pointer, + sub %al,0x2(%bp) # block count + ja read # If not done +read.6: retw # To caller +read.7: testb $FL_PACKET,%cs:MEM_REL+flags-start # LBA support enabled? + jz ereturn # No, so return an error + mov $0x55aa,%bx # Magic + push %dx # Save + movb $0x41,%ah # BIOS: Check + int $0x13 # extensions present + pop %dx # Restore + jc return # If error, return an error + cmp $0xaa55,%bx # Magic? + jne ereturn # No, so return an error + testb $0x1,%cl # Packet interface? + jz ereturn # No, so return an error + mov %bp,%si # Disk packet + movb $0x42,%ah # BIOS: Extended + int $0x13 # read + retw # To caller + +# Messages + +msg_read: .asciz "Rd" +msg_part: .asciz "Boot" + +prompt: .asciz " err\r\n" + +flags: .byte FLAGS # Flags + + .org PRT_OFF,0x90 + +# Partition table + + .fill 0x30,0x1,0x0 +part4: .byte 0x80 + .byte 0x00 # start head + .byte 0x01 # start sector (6 bits) + start cyl (2 bit) + .byte 0x00 # start cyl (low 8 bits) + .byte 0x1 # part.type + .byte 0xff # end head + .byte 0xff # end sect (6) + end_cyl(2) + .byte 0xff # end cyl + .byte 0x00, 0x00, 0x00, 0x00 # explicit start + .byte 0x50, 0xc3, 0x00, 0x00 # 50000 sectors long, bleh + + .word 0xaa55 # Magic number diff --git a/src/arch/i386/prefix/comprefix.S b/src/arch/i386/prefix/comprefix.S new file mode 100644 index 00000000..8be8db77 --- /dev/null +++ b/src/arch/i386/prefix/comprefix.S @@ -0,0 +1,49 @@ +/* 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 + .globl _prefix + +/* 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 + .globl prefix_exit +prefix_exit: + movw $0x4c00,%ax /* return to DOS */ + int $0x21 /* reach this on Quit */ + .globl prefix_exit_end +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/src/arch/i386/prefix/elf_dprefix.S b/src/arch/i386/prefix/elf_dprefix.S new file mode 100644 index 00000000..d2453be4 --- /dev/null +++ b/src/arch/i386/prefix/elf_dprefix.S @@ -0,0 +1,96 @@ +#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 + .globl prefix_exit +prefix_exit: + + .globl prefix_exit_end +prefix_exit_end: + .previous diff --git a/src/arch/i386/prefix/elfprefix.S b/src/arch/i386/prefix/elfprefix.S new file mode 100644 index 00000000..c5200678 --- /dev/null +++ b/src/arch/i386/prefix/elfprefix.S @@ -0,0 +1,96 @@ +#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 + .globl prefix_exit +prefix_exit: + + .globl prefix_exit_end +prefix_exit_end: + .previous diff --git a/src/arch/i386/prefix/exeprefix.S b/src/arch/i386/prefix/exeprefix.S new file mode 100755 index 00000000..d169563e --- /dev/null +++ b/src/arch/i386/prefix/exeprefix.S @@ -0,0 +1,44 @@ +/* + 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 + .globl _prefix + +_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 + .globl prefix_exit +prefix_exit: + movw $0x4c00,%ax /* return to DOS */ + int $0x21 /* reach this on Quit */ + .globl prefix_exit_end +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/src/arch/i386/prefix/floppyprefix.S b/src/arch/i386/prefix/floppyprefix.S new file mode 100644 index 00000000..18bed4c8 --- /dev/null +++ b/src/arch/i386/prefix/floppyprefix.S @@ -0,0 +1,359 @@ +/* NOTE: this boot sector contains instructions that need at least an 80186. + * Yes, as86 has a bug somewhere in the valid instruction set checks. + * + * SYS_SIZE is the number of clicks (16 bytes) to be loaded. + */ +.equ SYSSIZE, 8192 # 8192 * 16 bytes = 128kB maximum size of .ROM file + +/* 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 */ + + pushw %es /* = ds */ + 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 */ + pop %es /* balance push/pop es */ +sigok: + +/* 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) + +/* after that (everything loaded), we call to the .ROM file loaded. */ + + pushl $0 /* No parameters to preserve for exit path */ + pushw $0 /* Use prefix exit path mechanism */ + ljmp $SYSSEG, $_start + + .section ".text16", "ax", @progbits + .globl prefix_exit +prefix_exit: + xchgw %bx, %bx + int $0x19 /* should try to boot machine */ + .globl prefix_exit_end +prefix_exit_end: + .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 /* read whole image incl boot sector */ + 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 $SYSSEG+SYSSIZE, %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 + diff --git a/src/arch/i386/prefix/huf.lds b/src/arch/i386/prefix/huf.lds new file mode 100644 index 00000000..1e8da162 --- /dev/null +++ b/src/arch/i386/prefix/huf.lds @@ -0,0 +1,6 @@ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) + +SECTIONS { + .huf : { *(*) } +} diff --git a/src/arch/i386/prefix/img.lds b/src/arch/i386/prefix/img.lds new file mode 100644 index 00000000..e383d89e --- /dev/null +++ b/src/arch/i386/prefix/img.lds @@ -0,0 +1,6 @@ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) + +SECTIONS { + .img : { *(*) } +} diff --git a/src/arch/i386/prefix/liloprefix.S b/src/arch/i386/prefix/liloprefix.S new file mode 100644 index 00000000..1aeb071f --- /dev/null +++ b/src/arch/i386/prefix/liloprefix.S @@ -0,0 +1,144 @@ +/* + 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 + .globl _prefix +_prefix: + +/* + 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, $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: + pushl $0 /* No parameters to preserve for exit path */ + pushw $0 /* Use prefix exit path mechanism */ + /* 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)), $_start + + .section ".text16", "ax", @progbits + .globl prefix_exit +prefix_exit: + int $0x19 /* should try to boot machine */ + .globl prefix_exit_end +prefix_exit_end: + .previous + + .org (PREFIXSIZE-1) + .byte 0 +prefix_end: +/* + That's about it. +*/ + diff --git a/src/arch/i386/prefix/lmelf_dprefix.S b/src/arch/i386/prefix/lmelf_dprefix.S new file mode 100644 index 00000000..65fe1356 --- /dev/null +++ b/src/arch/i386/prefix/lmelf_dprefix.S @@ -0,0 +1,163 @@ +#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 + .globl prefix_exit +prefix_exit: + + .globl prefix_exit_end +prefix_exit_end: + .previous diff --git a/src/arch/i386/prefix/lmelf_prefix.S b/src/arch/i386/prefix/lmelf_prefix.S new file mode 100644 index 00000000..7488f452 --- /dev/null +++ b/src/arch/i386/prefix/lmelf_prefix.S @@ -0,0 +1,163 @@ +#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 + .globl prefix_exit +prefix_exit: + + .globl prefix_exit_end +prefix_exit_end: + .previous diff --git a/src/arch/i386/prefix/nullprefix.S b/src/arch/i386/prefix/nullprefix.S new file mode 100644 index 00000000..dbee141b --- /dev/null +++ b/src/arch/i386/prefix/nullprefix.S @@ -0,0 +1,16 @@ + .org 0 + .text + .arch i386 + + .section ".prefix", "ax", @progbits + .code16 + .globl _prefix +_prefix: + + .section ".text16", "ax", @progbits + .globl prefix_exit +prefix_exit: + + .globl prefix_exit_end +prefix_exit_end: + .previous diff --git a/src/arch/i386/prefix/pxeprefix.S b/src/arch/i386/prefix/pxeprefix.S new file mode 100644 index 00000000..246e22b2 --- /dev/null +++ b/src/arch/i386/prefix/pxeprefix.S @@ -0,0 +1,398 @@ +/* Offsets of words containing ROM's CS and size (in 512 byte blocks) + * from start of floppy boot block at 0x7c00 + * Offsets must match those in etherboot.h + */ +#define FLOPPY_SEGMENT 0x7c0 + +#define PXENV_UNDI_CLEANUP 0x02 +#define PXENV_UNDI_SHUTDOWN 0x05 +#define PXENV_STOP_UNDI 0x15 +#define PXENV_UNLOAD_STACK 0x70 +#define PXENV_STOP_BASE 0x76 + +#define PUSHA_SIZE 16 +#define PXE_STACK_MAGIC 0x57ac /* 'STac' */ + + .text + .code16 + .arch i386 + .org 0 + .section ".prefix", "ax", @progbits + .globl _prefix +/***************************************************************************** + * Entry point: set cs, ds, bp, print welcome message + ***************************************************************************** + */ +_prefix: + jmp $FLOPPY_SEGMENT, $code_start-_prefix +10: .asciz "PXE->EB " +code_start: + pusha /* Preserve all registers */ + push %ds + movw %sp, %bp /* %bp must be preserved, hence do + * this after the pusha */ + push $PXE_STACK_MAGIC /* PXE stack magic marker */ + + push %cs /* Set up data segment */ + pop %ds + mov $0x40, %cx /* Set up %fs for access to 40:13 */ + mov %cx, %fs + movw $10b-_prefix, %si /* Print welcome message */ + call print_message + +/***************************************************************************** + * Detect type of PXE available (!PXE, PXENV+ or none) + ***************************************************************************** + */ +detect_pxe: + les 4+PUSHA_SIZE+2(%bp), %di /* !PXE structure */ + cmpl $0x45585021, %es:(%di) /* '!PXE' signature */ + je detected_pxe + mov $0x5650, %ax + int $0x1a + cmp $0x564e, %ax + jne detected_nothing + cmpl $0x4e455850, %es:(%bx) /* 'PXEN' signature */ + jne detected_nothing + cmpw $0x2b56, %es:4(%bx) /* 'V+' signature */ + je detected_pxenv + +detected_nothing: + movw $10f-_prefix, %si + call print_message + jmp finished_with_error +10: .asciz "No PXE " + +detected_pxenv: /* es:bx points to PXENV+ structure */ + push %es + push %bx + push %es:0x24(%bx) /* UNDI code segment */ + push %es:0x26(%bx) /* UNDI code size */ + push %es:0x20(%bx) /* UNDI data segment */ + push %es:0x22(%bx) /* UNDI data size */ + les %es:0x0a(%bx), %di /* Entry point to %es:%di */ + movw $10f-_prefix, %si + jmp pxe_setup_done +10: .asciz "PXENV+ " + +detected_pxe: /* es:di points to !PXE structure */ + push %es + push %di + push %es:0x30(%di) /* UNDI code segment */ + push %es:0x36(%di) /* UNDI code size */ + push %es:0x28(%di) /* UNDI data segment */ + push %es:0x2e(%di) /* UNDI data size */ + les %es:0x10(%di), %di /* Entry point to %es:%di */ + movw $10f-_prefix, %si + jmp pxe_setup_done +10: .asciz "!PXE " + +pxe_setup_done: + mov %es, pxe_entry_segment - _prefix + mov %di, pxe_entry_offset - _prefix + pop %ax + mov %ax, undi_data_size - _prefix + pop %ax + mov %ax, undi_data_segment - _prefix + pop %ax + mov %ax, undi_code_size - _prefix + pop %ax + mov %ax, undi_code_segment - _prefix + call print_message + pop %di + pop %es /* Exit with %es:%di containing structure address */ + +/***************************************************************************** + * Print information about located structure + ***************************************************************************** + */ +print_structure_information: + call print_segoff /* %es:%di contains address of structure */ + les %ds:(pxe_entry_segoff - _prefix), %di + call print_segoff + les %ds:(undi_code_segoff - _prefix), %di + call print_segoff + les %ds:(undi_data_segoff - _prefix), %di + call print_segoff + +/***************************************************************************** + * Unload PXE base code and UNDI driver + ***************************************************************************** + */ +#ifdef PXELOADER_KEEP_ALL + xor %ax, %ax /* Force zero flag to show success */ + jmp do_not_free_base_mem /* Skip the unloading */ +#endif /* PXELOADER_KEEP_ALL */ + +unload_pxe: + mov $PXENV_UNLOAD_STACK, %bx + call pxe_call + mov $PXENV_STOP_UNDI, %bx + call pxe_call + pushfw /* Ignore PXENV_UNDI_CLEANUP errors */ + mov $PXENV_UNDI_CLEANUP, %bx + call pxe_call + popfw + /* On exit, zero flag is set iff all calls were successful */ + +/***************************************************************************** + * Free base memory + ***************************************************************************** + */ +free_base_mem: + jnz do_not_free_base_mem /* Using zero flag from unload_pxe */ + + mov undi_code_segment - _prefix, %bx + mov undi_data_segment - _prefix, %cx + mov undi_code_size - _prefix, %ax + cmp %bx, %cx + jb 1f + mov %cx, %bx + mov undi_data_size - _prefix, %ax +1: add $0x0f, %ax /* Round up to next segment */ + shr $4, %ax + add %bx, %ax /* Highest segment address into %ax */ + add $(1024 / 16 - 1), %ax /* Round up to next kb */ + shr $6, %ax /* New free basemem size in %ax */ + mov %fs:(0x13), %bx /* Old free base memory in %bx */ + mov %ax, %fs:(0x13) /* Store new free base memory size */ + + /* Note that zero_mem_loop will also zero out our stack, so make + * sure the stack is empty at this point. + */ + mov %ax, %dx + sub %bx, %dx /* numberof kb to zero in %dx */ + shl $6, %bx /* Segment address into %bx */ +zero_mem_loop: + mov %bx, %es /* kB boundary into %es:00 */ + xor %ax, %ax + xor %di, %di + mov $0x400, %cx + rep stosb /* fill kB with zeroes */ + add $(1024 / 16), %bx + dec %dx + jnz zero_mem_loop + /* Will exit here with zero flag set, so no need to set it explicitly + * in order to indicate success. + */ + +do_not_free_base_mem: + pushf /* Save success (zero) flag status */ + mov %fs:(0x13), %ax /* Free base memory in %ax */ + call print_hex_word /* Print free base memory */ + popf /* Restore success (zero) flag */ + +/***************************************************************************** + * Exit point + * Jump to finished with the zero flag set to indicate success, or to + * finished_with_error to always report an error + ***************************************************************************** + */ +finished: + movw $10f-_prefix, %si + jz 1f +finished_with_error: + movw $20f-_prefix, %si +1: + call print_message + jmp 99f +10: .asciz " ok\n" +20: .asciz " err\n" + + + /* We place a stack here. It doesn't get used until after all + * the above code is finished, so we can happily write all + * over it. Putting the stack here ensures that it doesn't + * accidentally go over the 512 byte threshold, which would + * cause problems when returning via start32's prefix + * relocation mechanism. + */ +_estack: +99: + +/***************************************************************************** + * Run Etherboot main code + ***************************************************************************** + */ +run_etherboot: + /* Very temporarily switch stacks to one internal to the + * prefix. Do this because the stack provided by the PXE ROM + * could be absolutely anywhere, including in an area of + * memory that the call to prelocate will vapourise... + */ + pushw %ss /* PXE stack pointer to ES:DI */ + popw %es + movw %sp, %di + pushw %ds /* Set up stack in "safe" area */ + popw %ss + movw $_estack-_prefix, %sp + pushw %es /* Record PXE stack pointer */ + pushw %di + /* Relocate payload and stack to claimed base memory */ + pushw $4 /* Preserve old PXE stack pointer */ + call prelocate + popw %ax /* Remove parameter */ + pushl $4 /* Preserve old PXE stack pointer */ + pushw $0 /* Indicate prefix exit mechanism */ + jmp _start /* Run Etherboot */ + + .section ".text16", "ax", @progbits + .globl prefix_exit +prefix_exit: + pushw %cs /* Set %ds, %bp for access to text */ + popw %ds + call 1f +1: popw %bp + popw %di /* Old PXE stack to %es:di */ + popw %es + cmpw $PXE_STACK_MAGIC, %es:0(%di) /* See if PXE stack intact */ + jne exit_via_int18 +exit_via_pxe: /* Stack OK, return to PXE */ + push %es /* Restore PXE stack pointer */ + pop %ss + mov %di, %sp + pop %ax /* Discard PXE_STACK_MAGIC marker */ + leaw (10f-1b)(%bp), %si + call print_exit_message + pop %ds /* Restore PXE's DS */ + popa /* Restore PXE's other registers */ + movw $0, %ax /* Return PXENV_STATUS_SUCCESS */ + lret /* Return control to PXE ROM */ +10: .asciz "EB->PXE\r\n" +exit_via_int18: /* Stack damaged, do int 18 */ + leaw (10f-1b)(%bp), %si + call print_exit_message + int $0x18 +10: .asciz "EB->BIOS\r\n" +print_exit_message: + movw $0x0007, %bx /* page 0, attribute 7 (normal) */ + movb $0x0e, %ah /* write char, tty mode */ +1: lodsb + testb %al, %al + je 2f + int $0x10 + jmp 1b +2: ret + .globl prefix_exit_end +prefix_exit_end: + .previous + +/***************************************************************************** + * Subroutine: print character in %al (with LF -> LF,CR translation) + ***************************************************************************** + */ +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 + ret + +/***************************************************************************** + * Subroutine: print a zero-terminated message starting at %si + ***************************************************************************** + */ +print_message: +1: lodsb + testb %al, %al + je 2f + call print_character + jmp 1b +2: ret + +/***************************************************************************** + * Subroutine: print hex word in %ax + ***************************************************************************** + */ +print_hex_word: + mov $4, %cx +1: + push %ax + shr $12, %ax + /* Courtesy of Norbert Juffa */ + cmp $10, %al + sbb $0x69, %al + das + call print_character + pop %ax + shl $4, %ax + loop 1b + ret + +/***************************************************************************** + * Subroutine: print segment:offset address in %es:%di + ***************************************************************************** + */ +print_segoff: + push %di + push %es + pop %ax + call print_hex_word + movb $0x3a,%al /* ':' */ + call print_character + pop %ax + call print_hex_word + mov $0x20, %al /* ' ' */ + call print_character + ret + +/***************************************************************************** + * Make a PXE API call. Works with either !PXE or PXENV+ API. + * Opcode in %bx. pxe_parameter_structure always used. + * Returns status code (not exit code) in %bx and prints it. + * ORs status code with overall status code in pxe_overall_status, returns + * with zero flag set iff all PXE API calls have been successful. + ***************************************************************************** + */ +pxe_call: + /* Set up registers for PXENV+ API. %bx already set up */ + push %ds + pop %es + mov $pxe_parameter_structure - _prefix, %di + /* Set up stack for !PXE API */ + pushw %cs + pushw %di + pushw %bx + /* Make the API call */ + lcall *(pxe_entry_segoff - _prefix) + /* Reset the stack */ + add $6, %sp + mov pxe_parameter_structure - _prefix, %ax + push %ax + call print_hex_word + mov $0x20, %ax /* ' ' */ + call print_character + pop %bx + or %bx, pxe_overall_status - _prefix + ret + +/***************************************************************************** + * PXE data structures + ***************************************************************************** + */ + +pxe_overall_status: .word 0 + +pxe_entry_segoff: +pxe_entry_offset: .word 0 +pxe_entry_segment: .word 0 + +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 + +pxe_parameter_structure: + .word 0 + .word 0,0,0,0,0 + +end_of_pxeloader: + + .balign 16, 0 +payload: diff --git a/src/arch/i386/prefix/romprefix.S b/src/arch/i386/prefix/romprefix.S new file mode 100644 index 00000000..c6c83489 --- /dev/null +++ b/src/arch/i386/prefix/romprefix.S @@ -0,0 +1,416 @@ +/* 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 DELAYED_INT when NO_DELAYED_INT is not defined. + * This allows positive tests instead of tests that contain + * double negatives, and become confusing. + */ +#ifndef NO_DELAYED_INT +#define DELAYED_INT +#endif + +/* We need some unique magic ID, if we defer startup thru the INT18H or INT19H + * handler. This way, we can check if we have already been installed. + */ +#ifndef MAGIC +#define MAGIC 0xE44C +#endif + +/* Hook into INT18H or INT19H handler */ +#ifdef BOOT_INT18H +#define BOOT_INT 0x18 +#else +#define BOOT_INT 0x19 +#endif + +#define BOOT_INT_VEC BOOT_INT*4 +#define SCRATCHVEC 0x300 + +/* Prefix exit codes. We store these on the stack so that we will + * know how to return control to the BIOS when Etherboot exits. + */ +#define EXIT_VIA_LRET 0x0 +#define EXIT_VIA_INT_18 0x1 +#define EXIT_VIA_BOOT_INT 0x2 + + .text + .code16 + .arch i386 + .org 0 + .section ".prefix", "ax", @progbits + .globl _prefix +_prefix: + .word 0xAA55 /* BIOS extension signature */ +size: .byte 0 /* number of 512 byte blocks */ + /* = number of 256 word blocks */ + /* filled in by makerom program */ + jmp over /* skip over checksum */ + .byte 0 /* checksum */ + jmp legacyentry /* alternate entry point +6 */ + /* used by mknbi-rom */ + +#ifdef PCI_PNP_HEADER +mfgstr: + .asciz "Etherboot" + +#ifdef PXE_EXPORT + .org 0x16 + .word UNDIROMID - _prefix +#endif /* PXE_EXPORT */ + + .org 0x18 + .word PCI - _prefix + .word PnP - _prefix + +PCI: + .ascii "PCIR" + .word 0x0000 /* vendor ID, filled in by makerom */ + .word 0x0000 /* device ID, filled in by makerom */ + .word 0x0000 /* pointer to vital product data */ + .word 0x0018 /* 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 */ + .word 0x0000 /* Image length same as offset 02h */ + .word 0x0001 /* revision level of code/data */ + .byte 0x00 /* code type */ + .byte 0x80 /* indicator (last PCI data structure) */ + .word 0x0000 /* reserved */ + +PnP: + .ascii "$PnP" + .byte 0x01 /* structure revision */ + .byte 0x02 /* length (in 16 byte increments) */ + .word 0x0000 /* offset of next header */ + .byte 0x00 /* Reserved */ + .byte 0x00 /* checksum filled by makerom */ + .long 0x00000000 /* Device identifier */ + .word mfgstr - _prefix + .word 0x0 /* pointer to product name */ + /* filled by makerom */ + .byte 0x02 /* Device Base Type code */ + .byte 0x00 /* Device Sub-Type code */ + .byte 0x00 /* Device Interface Type code */ + .byte 0x14 /* device indicator */ + .word 0x0000 /* boot connection vector */ + .word 0x0000 /* disconnect vector */ + .word pnpentry - _prefix + .word 0x0000 /* reserved */ + .word 0x0000 /* static resource information vector */ +#ifdef PXE_EXPORT +UNDIROMID: + .ascii "UNDI" + .byte UNDIROMID_end - UNDIROMID /* length of structure */ + .byte 0 /* Checksum */ + .byte 0 /* Structure revision */ + .byte 0,1,2 /* PXE version 2.1.0 */ + .word UNDILoader - _prefix /* Offset to loader routine */ + .word UNDIStackSize /* Stack segment size */ + .word UNDIDataSize /* Data segment size */ + .word UNDICodeSize /* Code segment size */ + .ascii "PCIR" + + /* The code segment contains our pxe_stack_t plus the PXE and + * RM callback interfaces. We don't actually use a data + * segment, but we put a nonzero value here to avoid confusing + * things. 16k of stack space should be enough. + * + * When we claim our own memory, we fill out the data segment + * with the address and size of the real-mode stack, so that + * NBPs will free that area of memory for us. When the UNDI + * loader is used to initialise us, we will never need a + * real-mode stack because we will only ever be called via the + * PXE API, hence our stack is already in base memory. + */ + .equ UNDICodeSize, _pxe_stack_size + .equ UNDIDataSize, _real_mode_stack_size + .equ UNDIStackSize, _real_mode_stack_size +UNDIROMID_end: +#endif /* PXE_EXPORT */ + +#endif /* PCI_PNP_HEADER */ + +/* + * Explicitly specify DI is wrt ES to avoid problems with some BIOSes + * Discovered by Eric Biederman + * In addition, some BIOSes don't point DI to the string $PnP so + * we need another #define to take care of that. + */ +over: +#ifdef DEBUG_ROMPREFIX + call print_bcv +#endif +/* Omit this test for ISA cards anyway */ +#ifdef PCI_PNP_HEADER +/* Accept old name too for backward compatibility */ +#if !defined(BBS_BUT_NOT_PNP_COMPLIANT) && !defined(PNP_BUT_NOT_BBS_COMPLIANT) + cmpw $'$'+'P'*256,%es:0(%di) + jne notpnp + cmpw $'n'+'P'*256,%es:2(%di) + jne notpnp +#endif /* BBS_BUT_NOT_PNP_COMPLIANT */ + movw $0x20,%ax + lret +#endif /* PCI_PNP_HEADER */ +notpnp: +#ifdef DEBUG_ROMPREFIX + call print_notpnp +#endif +#ifdef DELAYED_INT + pushw %ax + pushw %ds + xorw %ax,%ax + movw %ax,%ds /* access first 64kB segment */ + movw SCRATCHVEC+4, %ax /* check if already installed */ + cmpw $MAGIC, %ax /* check magic word */ + jz installed + movw BOOT_INT_VEC, %ax /* hook into INT18H or INT19H */ + movw %ax, SCRATCHVEC + movw BOOT_INT_VEC+2, %ax + movw %ax, SCRATCHVEC+2 + movw $start_int - _prefix, %ax + movw %ax, BOOT_INT_VEC + movw %cs,%ax + movw %ax, BOOT_INT_VEC+2 + movw $MAGIC, %ax /* set magic word */ + movw %ax, SCRATCHVEC+4 +#ifdef DEBUG_ROMPREFIX + call print_installed +#endif +installed: + popw %ds + popw %ax + movw $0x20,%ax + lret + +start_int: /* clobber magic id, so that we will */ +#ifdef DEBUG_ROMPREFIX + call print_start_int +#endif + xorw %ax,%ax /* not inadvertendly end up in an */ + movw %ax,%ds /* endless loop */ + movw %ax, SCRATCHVEC+4 + movw SCRATCHVEC+2, %ax /* restore original INT19h handler */ + movw %ax, BOOT_INT_VEC+2 + movw SCRATCHVEC, %ax + movw %ax, BOOT_INT_VEC + pushl %eax /* padding */ + pushw $EXIT_VIA_BOOT_INT + jmp invoke +#endif /* DELAYED_INT */ + + + + +legacyentry: +#ifdef DEBUG_ROMPREFIX + call print_legacyentry +#endif + pushw $EXIT_VIA_LRET + jmp invoke + + + +#ifdef PCI_PNP_HEADER +pnpentry: +#ifdef DEBUG_ROMPREFIX + call print_bev +#endif + pushl %eax /* padding */ + pushw $EXIT_VIA_INT_18 + jmp invoke +#endif /* PCI_PNP_HEADER */ + + +invoke: + /* Store ROM segment and size on stack */ + pushw %ax + pushw %ds + pushw %cs + movzbw %cs:(size-_prefix), %ax + shlw $9, %ax /* 512-byte blocks */ + pushw %ax + /* Relocate to free base memory, switch stacks */ + pushw $12 /* Preserve exit code & far ret addr */ + call prelocate + /* We are now running in RAM */ + popw %ax /* padding */ + movw %cs, %ax + movw %ax, %ds + popw %ds:(_prefix_rom+2) /* ROM size */ + popw %ds:(_prefix_rom+0) /* ROM segment */ + popw %ds /* Original %ds */ + popw %ax /* Original %ax */ + pushw %ax /* 4-byte alignment */ + pushl $8 /* Preserve exit code & far ret addr */ + pushw $0 /* Set null return address */ + jmp _start + + + .section ".text16", "ax", @progbits + .globl prefix_exit +prefix_exit: + popw %ax /* padding */ + popw %ax /* %ax = exit code */ + cmpw $EXIT_VIA_LRET, %ax + jne 1f + /* Exit via LRET */ + lret +1: addw $4, %sp /* Strip padding */ + cmpw $EXIT_VIA_BOOT_INT, %ax + jne 2f + /* Exit via int BOOT_INT */ + int $BOOT_INT /* Try original vector */ +2: /* Exit via int $0x18 */ + int $0x18 /* As per BIOS Boot Spec, next dev */ + .globl prefix_exit_end +prefix_exit_end: + .previous + + + +#ifdef PXE_EXPORT + +#include "callbacks.h" +#define PXENV_UNDI_LOADER 0x104d + + .section ".prefix" +UNDILoader: + /* Loader API is different to the usual PXE API; there is no + * opcode on the stack. We arrange the stack to look like a + * normal PXE API call; this makes the Etherboot internals + * cleaner and avoids adding an extra API type just for the + * PXE loader. + */ + pushw %bx + movw %sp, %ax /* Store original %ss:sp */ + pushw %ss + pushw %ax + pushl %eax /* Space for loader structure ptr */ + pushw %bp + movw %sp, %bp + movw 16(%bp), %ax /* Copy loader structure ptr */ + movw %ax, 2(%bp) + movw 18(%bp), %ax + movw %ax, 4(%bp) + popw %bp + pushw $PXENV_UNDI_LOADER /* PXE 'opcode' */ + pushl %eax /* dummy return address */ + /* Stack now looks like a normal PXE API call */ + /* Store ROM segment and size on stack */ + pushw %ax + pushw %cs + movzbw %cs:(size-_prefix), %ax + shlw $9, %ax /* 512-byte blocks */ + pushw %ax + /* Unpack Etherboot into temporarily claimed base memory */ + pushw $20 /* Dummy ret, PXE params, orig ss:sp */ + call prelocate + popw %ax /* discard */ + popw %cs:(_prefix_rom+2) /* ROM size */ + popw %cs:(_prefix_rom+0) /* ROM segment */ + popw %ax /* Original %ax */ + /* Inhibit automatic deallocation of base memory */ + movl $0, %cs:_prefix_image_basemem + /* Make PXE API call to Etherboot */ + pushl $0x201 /* PXE API version */ + /* Need to USE_INTERNAL_STACK, since we will call relocate() */ + pushl $(EB_OPCODE_PXE|EB_USE_INTERNAL_STACK) /* PXE API call type */ + call _entry + addw $18, %sp /* discard */ + popw %bx /* Restore original %ss:sp */ + popw %ss + movw %bx, %sp + popw %bx + call deprelocate + lret $2 /* Skip our PXE 'opcode' */ +#endif /* PXE_EXPORT */ + +#ifdef DEBUG_ROMPREFIX + .section ".prefix" + +print_bcv: + pushw %si + movw $1f-_prefix, %si + call print_message + popw %si + ret +1: .asciz "ROM detected\r\n" + +print_bev: + pushw %si + movw $1f-_prefix, %si + call print_message + popw %si + ret +1: .asciz "booting\r\n" + +print_notpnp: + pushw %si + movw $1f-_prefix, %si + call print_message + popw %si + ret +1: .asciz ": Non-PnP BIOS detected!\r\n" + +print_legacyentry: + pushw %si + movw $1f-_prefix, %si + call print_message + popw %si + ret +1: .asciz "ROM using legacy boot mechanism\r\n" + +print_installed: + pushw %si + movw $1f-_prefix, %si + call print_message + popw %si + ret +1: .ascii "hooked boot via INT" +#ifdef BOOT_INT18H + .asciz "18\r\n" +#else + .asciz "19\r\n" +#endif + +print_start_int: + pushw %si + movw $1f-_prefix, %si + call print_message + popw %si + ret +1: .asciz "booting via hooked interrupt\r\n" + +print_message: + pushaw + pushw %ds + pushw %cs + popw %ds + pushw %si + movw $1f-_prefix, %si + call print_string + popw %si + call print_string + popw %ds + popaw + ret +1: .asciz "Etherboot " + +print_string: +1: lodsb + testb %al,%al + je 2f + movw $0x0007, %bx /* page 0, attribute 7 (normal) */ + movb $0x0e, %ah /* write char, tty mode */ + int $0x10 + jmp 1b +2: ret + +#endif diff --git a/src/arch/i386/prefix/unhuf.S b/src/arch/i386/prefix/unhuf.S new file mode 100644 index 00000000..390556e3 --- /dev/null +++ b/src/arch/i386/prefix/unhuf.S @@ -0,0 +1,400 @@ +/***************************************************************************** + * NOTE: This code is no longer used in Etherboot. The obsolete + * Makefile target .lzrom refers to it, but it is no longer being + * maintained and may no longer work. Use .zrom instead (which uses + * the unnrv2b decompressor). + ***************************************************************************** + */ + +/* 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. + */ + + +/* LZHuf (LZSS) Decompressing boot loader for ROM images + * + * this code is based on the work of Haruyasu Yoshizaki and Haruhiko Okumura + * who implemented the original compressor and decompressor in C code + * + * Converted to 32bit assembly 16 July 2002 Eric Biederman + * Made PIC 10 Aug 2002 Eric Biederman + * + * Copyright 1997 by M. Gutschke + * + * Compression pays off, as soon as the uncompressed image is bigger than + * about 1.5kB. This assumes an average compressibility of about 60%. + */ + + +/* 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 compressor accordingly. + */ +#define BUFSZ 4096 +#define LOOKAHEAD 60 +#define THRESHOLD 2 +#define NCHAR (256+LOOKAHEAD-THRESHOLD) +#define TABLESZ (NCHAR+NCHAR-1) +#define ROOT (TABLESZ-1) + + .text + .arch i386 + .globl _start +_start: + cli + + /* Save the initial register values */ + pushal + + /* + * See where I am running, and compute %ebp + */ + call 1f +1: pop %ebp + subl $1b, %ebp + +/* + * INIT -- initializes all data structures + * ==== + */ + +init: + cld + leal dcodrle(%ebp), %esi /* uncompress run length encoded */ + leal dcode(%ebp), %edi /* lookup table for codes */ + movb $6, %dl + movb $0x20, %dh + xorb %bh,%bh +init0: + lodsb + movb %al,%bl +init1: + xorl %ecx, %ecx + movb %dh,%cl + movb %bh,%al + rep + stosb + incb %bh + decb %bl + jnz init1 + shrb %dh + decb %dl + jnz init0 + movb $1, %bl /* uncompress run length encoded */ + movb $6, %bh /* lookup table for code lengths */ +init2: + lodsb + xorl %ecx, %ecx + movb %al,%cl + movb %bl,%al + rep + stosb + incb %bl + decb %bh + jnz init2 + + movl $NCHAR, %ecx /* set all frequencies of leaf nodes */ + movw $1, %ax /* to one */ + rep + stosw + leal freq(%ebp), %esi + movl $ROOT+1-NCHAR, %ecx +init3: + lodsw /* update frequencies of non-leaf nodes */ + movw %ax,%bx + lodsw + addw %bx,%ax + stosw + loop init3 + movw $0xFFFF, %ax + stosw /* sentinel with infinite frequency */ + movl $NCHAR, %ecx + movw $TABLESZ, %ax +init4: + stosw /* update son pointers for leaf nodes */ + incw %ax + loop init4 + movl $ROOT+1-NCHAR, %ecx + xorw %ax,%ax +init5: + stosw /* update son ptrs for non-leaf nodes */ + addw $2, %ax + loop init5 + movl $ROOT+1-NCHAR, %ecx + movw $NCHAR, %ax +init6: + stosw /* update parent ptrs for non-leaf nd. */ + stosw + incw %ax + loop init6 + movl $NCHAR, %ecx + xorw %ax,%ax + stosw /* root node has no parent */ +init7: + stosw /* update parent ptrs for leaf nodes */ + incw %ax + loop init7 + xorw %ax,%ax + stosb /* clear getlen */ + stosw /* clear getbuf */ + movb $0x20, %al /* fill text buffer with spaces */ + leal spaces(%ebp), %edi + movl $BUFSZ-LOOKAHEAD, %ecx + rep + + stosb + /* fall thru */ + +/* + * MAIN -- reads compressed codes and writes decompressed data + * ==== + */ + + leal _payload(%ebp), %esi /* get length of compressed data stream */ + leal uncompressed(%ebp), %edi + + lodsl + movl %eax, %ecx +main1: + pushl %ecx + call dcdchr /* decode one code symbol */ + orb %ah,%ah /* test if 8bit character */ + jnz main2 + stosb /* store verbatim */ + popl %ecx + loop main1 /* proceed with next compressed code */ + jmp done /* until end of input is detected */ +main2: + pushl %eax + call dcdpos /* compute position in output buffer */ + movl %esi, %eax + subl %edi, %ebx + notl %ebx + movl %ebx, %esi /* si := di - dcdpos() - 1 */ + popl %ecx + subl $255-THRESHOLD, %ecx /* compute length of code sequence */ + movl %ecx, %edx + rep + movsb + movl %eax,%esi + popl %ecx + subl %edx, %ecx /* check end of input condition */ + jnz main1 /* proceed with next compressed code */ +done: + /* Start Etherboot */ + popal + jmp uncompressed +/* + * GETBIT -- gets one bit pointed to by DS:ESI + * ====== + * + * changes: AX,CX,DL + */ + +getbit: + movb $8, %cl + movb getlen(%ebp), %dl /* compute number of bits required */ + subb %dl,%cl /* to fill read buffer */ + jae getbit1 + movw getbuf(%ebp), %ax /* there is still enough read ahead data */ + jmp getbit2 +getbit1: + lodsb /* get next byte from input stream */ + xorb %ah,%ah + shlw %cl,%ax /* shift, so that it will fit into */ + movw getbuf(%ebp), %cx /* read ahead buffer */ + orw %cx,%ax + addb $8, %dl /* update number of bits in buffer */ +getbit2: + movw %ax,%cx + shlw %cx /* extract one bit from buffer */ + movw %cx, getbuf(%ebp) + decb %dl + movb %dl, getlen(%ebp) /* and update number of bits */ + shlw %ax /* return in carry flag */ + ret + + +/* + * DCDPOS -- decodes position in textbuffer as pointed to by DS:SI, result in BX + * ====== + * + * changes: AX,EBX,ECX,DX + */ + +dcdpos: + movl $0x0800, %ebx +dcdpos1: + shlb %bl /* read one byte */ + call getbit + jnc dcdpos2 + incb %bl +dcdpos2: + decb %bh + jnz dcdpos1 + movb %bl,%dh /* read length of code from table */ + xorb %bh,%bh + xorl %ecx, %ecx + movb dlen(%ebx, %ebp),%cl + movb dcode(%ebx, %ebp),%bl /* get top six bits from table */ + shll $6, %ebx +dcdpos3: + pushl %ecx /* read the rest from the input stream */ + shlb %dh + call getbit + jnc dcdpos4 + incb %dh +dcdpos4: + popl %ecx + loop dcdpos3 + andb $0x3f, %dh /* combine upper and lower half of code */ + orb %dh,%bl + ret + +/* + * DCDCHR -- decodes one compressed character pointed to by DS:SI + * ====== + * + * changes: AX,BX,CX,DX + */ + +dcdchr: + movl $ROOT, %ebx /* start at root entry */ + shll %ebx + movzwl son(%ebx, %ebp),%ebx +dcdchr1: + call getbit /* get a single bit */ + jnc dcdchr2 + incl %ebx /* travel left or right */ +dcdchr2: + shll %ebx + movzwl son(%ebx, %ebp), %ebx + cmpl $TABLESZ, %ebx /* until we come to a leaf node */ + jb dcdchr1 + movl %ebx, %eax + subl $TABLESZ, %eax + /* fall thru */ + +/* + * UPDATE -- updates huffman tree after incrementing frequency for code in BX + * ====== + * + * changes: BX,CX,DX + */ + +update: + /* we do not check whether the frequency count has overrun. + * this will cause problems for large files, but be should be fine + * as long as the compressed size does not exceed 32kB and we + * cannot do more than this anyways, because we load into the + * upper 32kB of conventional memory + */ + pushl %esi + pushl %eax + shll %ebx + movzwl parent(%ebx, %ebp),%ebx +update1: + shll %ebx + movzwl freq(%ebx, %ebp), %edx + incl %edx /* increment frequency count by one */ + movw %dx, freq(%ebx, %ebp) + leal 2+freq(%ebx, %ebp), %esi + lodsw /* check if nodes need reordering */ + cmpw %ax, %dx + jbe update5 +update2: + lodsw + cmpw %dx, %ax + jb update2 + movzwl -4(%esi), %ecx + movw %cx, freq(%ebx, %ebp) /* swap frequency of entries */ + movw %dx, -4(%esi) + + movl %esi, %eax /* compute index of new entry */ + subl $freq+4, %eax + subl %ebp, %eax + + movl %eax, %edx + shrl %eax + movzwl son(%ebx, %ebp), %ecx /* get son of old entry */ + movl %ecx, %esi + addl %esi, %esi + movw %ax, parent(%esi, %ebp) /* and update the ptr to new parent */ + cmpl $TABLESZ, %ecx + jae update3 /* do this for both branches */ + movw %ax, parent+2(%esi, %ebp) /* if not a leaf node */ +update3: + movl %edx, %esi + movzwl son(%esi, %ebp), %edx /* get son of new entry */ + movw %cx, son(%esi, %ebp) /* update its contents */ + movl %edx, %esi + addl %esi, %esi + movl %ebx, %ecx + shrl %ecx + movw %cx, parent(%esi, %ebp) /* and update the ptr to new paren */ + cmpl $TABLESZ, %edx + jae update4 /* do this for both branches */ + movw %cx, parent+2(%esi, %ebp) /* if not a leaf node */ +update4: + movw %dx, son(%ebx, %ebp) /* update son of old entry */ + movl %eax, %ebx /* continue with new entry */ + shll %ebx +update5: + movzwl parent(%ebx, %ebp), %ebx /* continue with parent */ + orl %ebx, %ebx + jnz update1 /* until we found the root entry */ + popl %eax + popl %esi + ret + +/* + * constant data. this part of the program resides in ROM and cannot be + * changed + * + * run length encoded tables will be uncompressed into the bss segment + * take care with any symbols here for .com files to add 0x100 to address + */ + +dcodrle: .byte 0x01,0x03,0x08,0x0C,0x18,0x10 +dlenrle: .byte 0x20,0x30,0x40,0x30,0x30,0x10 + +/* + * variable data segment (bss) + * this segment will always be found at 0x90000 (i.e. at RELOC - SCRATCH) + * + * do not change the order or the sizes of any of the following tables + * the initialization code makes assumptions on the exact layout of the + * data structures... + */ + +.bss +/* lookup table for index into buffer of recently output characters */ +dcode: .skip 256 + +/* lookup table for length of code sequence from buffer of recent characters */ +dlen: .skip 256 + +/* table with frequency counts for all codes */ +freq: .skip 2*(TABLESZ+1) + +/* pointer to child nodes */ +son: .skip 2*(TABLESZ) + +/* the first part of this table contains all the codes (0..TABLESZ-1) */ +/* the second part contains all leaf nodes (TABLESZ..) */ +parent: .skip 2*(TABLESZ+NCHAR) + +/* temporary storage for extracting bits from compressed data stream */ +getlen: .skip 1 +getbuf: .skip 1 + + /* the initial buffer has to be filled with spaces */ + .balign 4 +spaces: + .skip BUFSZ - LOOKAHEAD + /* uncompressed data will be written here */ +uncompressed: + diff --git a/src/arch/i386/prefix/unhuf.lds b/src/arch/i386/prefix/unhuf.lds new file mode 100644 index 00000000..00d6b55b --- /dev/null +++ b/src/arch/i386/prefix/unhuf.lds @@ -0,0 +1,33 @@ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) + +SECTIONS +{ + . = 0; + .text : { + _text = .; + *(.head) + *(.text) + } = 0x9090 + .rodata : { + *(.rodata) + } + _etext = . ; + .data : { + *(.data) + /* Force 4 byte alignment */ + . = ALIGN(4); + _payload = . ; + *(.huf) + _epayload = . ; + } + _edata = . ; + _data_size = _edata - _start; + /* Etherboot needs to be 16 byte aligned */ + . = ALIGN(16); + .bss : { + *(.bss) + } + _end = . ; + _image_size = _end - _start; +} diff --git a/src/arch/i386/prefix/unnrv2b.S b/src/arch/i386/prefix/unnrv2b.S new file mode 100644 index 00000000..1836fa71 --- /dev/null +++ b/src/arch/i386/prefix/unnrv2b.S @@ -0,0 +1,129 @@ +/* + * 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 + */ + + + .text + .arch i386 + .section ".prefix", "ax", @progbits + .code32 + + .globl decompress +decompress: + /* Save the initial register values */ + pushal + + /* + * See where I am running, and compute %ebp + * %ebp holds delta between physical and virtual addresses. + */ + call 1f +1: popl %ebp + subl $1b, %ebp + + /* "compressed" and "decompress_to" defined by linker script */ + /* move compressed image up to temporary area before decompressing */ + std + movl $_compressed_size, %ecx + leal _compressed+4-1(%ebp, %ecx), %esi + leal _compressed_copy-1(%ebp, %ecx), %edi + rep movsb + /* Setup to run the decompressor */ + cld + leal _compressed_copy(%ebp), %esi + leal decompress_to(%ebp), %edi + movl $-1, %ebp /* last_m_off = -1 */ + jmp dcl1_n2b + +/* ------------- DECOMPRESSION ------------- + + Input: + %esi - source + %edi - dest + %ebp - -1 + cld + + Output: + %eax - 0 + %ecx - 0 +*/ + +.macro getbit bits +.if \bits == 1 + addl %ebx, %ebx + jnz 1f +.endif + movl (%esi), %ebx + subl $-4, %esi /* sets carry flag */ + adcl %ebx, %ebx +1: +.endm + +decompr_literals_n2b: + movsb + +decompr_loop_n2b: + addl %ebx, %ebx + jnz dcl2_n2b +dcl1_n2b: + getbit 32 +dcl2_n2b: + jc decompr_literals_n2b + xorl %eax, %eax + incl %eax /* m_off = 1 */ +loop1_n2b: + getbit 1 + adcl %eax, %eax /* m_off = m_off*2 + getbit() */ + getbit 1 + jnc loop1_n2b /* while(!getbit()) */ + xorl %ecx, %ecx + subl $3, %eax + jb decompr_ebpeax_n2b /* if (m_off == 2) goto decompr_ebpeax_n2b ? */ + shll $8, %eax + movb (%esi), %al /* m_off = (m_off - 3)*256 + src[ilen++] */ + incl %esi + xorl $-1, %eax + jz decompr_end_n2b /* if (m_off == 0xffffffff) goto decomp_end_n2b */ + movl %eax, %ebp /* last_m_off = m_off ?*/ +decompr_ebpeax_n2b: + getbit 1 + adcl %ecx, %ecx /* m_len = getbit() */ + getbit 1 + adcl %ecx, %ecx /* m_len = m_len*2 + getbit()) */ + jnz decompr_got_mlen_n2b /* if (m_len == 0) goto decompr_got_mlen_n2b */ + incl %ecx /* m_len++ */ +loop2_n2b: + getbit 1 + adcl %ecx, %ecx /* m_len = m_len*2 + getbit() */ + getbit 1 + jnc loop2_n2b /* while(!getbit()) */ + incl %ecx + incl %ecx /* m_len += 2 */ +decompr_got_mlen_n2b: + cmpl $-0xd00, %ebp + adcl $1, %ecx /* m_len = m_len + 1 + (last_m_off > 0xd00) */ + pushl %esi + leal (%edi,%ebp), %esi /* m_pos = dst + olen + -m_off */ + rep + movsb /* dst[olen++] = *m_pos++ while(m_len > 0) */ + popl %esi + jmp decompr_loop_n2b +decompr_end_n2b: + /* Restore the initial register values */ + popal + ret diff --git a/src/arch/ia64/Config b/src/arch/ia64/Config new file mode 100644 index 00000000..6b7405f1 --- /dev/null +++ b/src/arch/ia64/Config @@ -0,0 +1,22 @@ +# Config for ia64 Etherboot +# +# Do not delete the tag OptionDescription and /OptionDescription +# It is used to automatically generate the documentation. +# +# @OptionDescrition@ +# +# BIOS interface options: +# +# -DCONFIG_EFI +# Compile in support for EFI +# +# @/OptionDescription@ + +CFLAGS+= -DCONSOLE_FIRMWARE +CFLAGS+= -DCONFIG_EFI + +CFLAGS+= -fpic -mconstant-gp -mauto-pic +ASFLAGS+= -mconstant-gp -mauto-pic + +LDFLAGS+= -static -shared -Bsymbolic --warn-multiple-gp --warn-common + diff --git a/src/arch/ia64/Makefile b/src/arch/ia64/Makefile new file mode 100644 index 00000000..42e0b47b --- /dev/null +++ b/src/arch/ia64/Makefile @@ -0,0 +1,125 @@ +ARCH_FORMAT= elf64-ia64-little + +LCONFIG+= + + +BUILD_EFIS= $(patsubst %.img, %.efi, $(IMGS)) $(patsubst %.img, %.zefi, $(IMGS)) + +START= $(BIN)/start.o $(BIN)/reloc.o +#START+= $(BIN)/efi_main.o + +SRCS+= arch/ia64/prefix/efi_prefix.S arch/ia64/prefix/unnrv2b.S +SRCS+= arch/ia64/core/__call.S +SRCS+= arch/ia64/core/ia64_timer.c +SRCS+= arch/ia64/core/idiv32.S +SRCS+= arch/ia64/core/idiv64.S +SRCS+= arch/ia64/core/longjmp.S +SRCS+= arch/ia64/core/memmove.S +SRCS+= arch/ia64/core/memset.S +SRCS+= arch/ia64/core/pal.c +SRCS+= arch/ia64/core/pci_io.c +SRCS+= arch/ia64/core/reloc.S +SRCS+= arch/ia64/core/relocate_to.S +SRCS+= arch/ia64/core/sal.c +SRCS+= arch/ia64/core/setjmp.S +SRCS+= arch/ia64/core/start.S +SRCS+= arch/ia64/core/efi.c + +ROMLIMIT:=3276800 + +include $(BIN)/Roms + +# We need allefis because $(IMGS) is not defined until +# the Makefile fragment "Roms" is read. +allefis: $(BUILD_EFIS) + + +#BOBJS+= $(BIN)/acpi.o +BOBJS+= $(BIN)/sal.o $(BIN)/pal.o +BOBJS+= $(BIN)/efi.o +BOBJS+= $(BIN)/memset.o $(BIN)/memmove.o +BOBJS+= $(BIN)/setjmp.o $(BIN)/longjmp.o +BOBJS+= $(BIN)/relocate_to.o $(BIN)/__call.o +BOBJS+= $(BIN)/pci_io.o $(BIN)/ia64_timer.o +BOBJS+= $(BIN)/__divdi3.o $(BIN)/__udivdi3.o $(BIN)/__moddi3.o $(BIN)/__umoddi3.o +BOBJS+= $(BIN)/__divsi3.o $(BIN)/__udivsi3.o $(BIN)/__modsi3.o $(BIN)/__umodsi3.o + + +# IA64 Division routines +$(BIN)/__divdi3.o: arch/ia64/core/idiv64.S + $(CPP) $(CFLAGS) -DASSEMBLY $< | $(AS) $(ASFLAGS) -o $@ + +$(BIN)/__udivdi3.o: arch/ia64/core/idiv64.S + $(CPP) $(CFLAGS) -DASSEMBLY -DUNSIGNED $< | $(AS) $(ASFLAGS) -o $@ + +$(BIN)/__moddi3.o: arch/ia64/core/idiv64.S + $(CPP) $(CFLAGS) -DASSEMBLY -DMODULO $< | $(AS) $(ASFLAGS) -o $@ + +$(BIN)/__umoddi3.o: arch/ia64/core/idiv64.S + $(CPP) $(CFLAGS) -DASSEMBLY -DUNSIGNED -DMODULO $< | $(AS) $(ASFLAGS) -o $@ + +$(BIN)/__divsi3.o: arch/ia64/core/idiv32.S + $(CPP) $(CFLAGS) -DASSEMBLY $< | $(AS) $(ASFLAGS) -o $@ + +$(BIN)/__udivsi3.o: arch/ia64/core/idiv32.S + $(CPP) $(CFLAGS) -DASSEMBLY -DUNSIGNED $< | $(AS) $(ASFLAGS) -o $@ + +$(BIN)/__modsi3.o: arch/ia64/core/idiv32.S + $(CPP) $(CFLAGS) -DASSEMBLY -DMODULO $< | $(AS) $(ASFLAGS) -o $@ + +$(BIN)/__umodsi3.o: arch/ia64/core/idiv32.S + $(CPP) $(CFLAGS) -DASSEMBLY -DUNSIGNED -DMODULO $< | $(AS) $(ASFLAGS) -o $@ + + + +# Utilities +$(BIN)/nrv2b: util/nrv2b.c + $(HOST_CC) -O2 -DENCODE -DDECODE -DMAIN -DVERBOSE -DNDEBUG -DBITSIZE=64 -DENDIAN=0 -o $@ $< + +# Pattern Rules + +# General for compiling assembly source files +$(BIN)/%.o: arch/ia64/core/%.c $(MAKEDEPS) + $(CC) $(CFLAGS) -o $@ -c $< + +$(BIN)/%.o: arch/ia64/prefix/%.S $(MAKEDEPS) + $(CPP) $(CFLAGS) -DASSEMBLY $< | $(AS) $(ASFLAGS) -o $@ + +$(BIN)/%.o: arch/ia64/core/%.S $(MAKEDEPS) + $(CPP) $(CFLAGS) -DASSEMBLY $< | $(AS) $(ASFLAGS) -o $@ + +$(BIN)/%.o: $(BIN)/%.s + $(AS) $(ASFLAGS) -o $@ $< + +# General rules for bootable images + +# Rules for nrv2b compressed images +$(BIN)/unnrv2b.tmp: $(BIN)/unnrv2b.o arch/ia64/prefix/unnrv2b.lds $(MAKEDEPS) + $(LD) -T arch/ia64/prefix/unnrv2b.lds $< -o $@ + +$(BIN)/unnrv2b: $(BIN)/unnrv2b.tmp $(MAKEDEPS) + $(OBJCOPY) -O binary $< $@ + +$(BIN)/%.zimg: $(BIN)/%.z $(BIN)/unnrv2b arch/ia64/prefix/apply_unnrv2b_prefix.pl $(MAKEDEPS) + $(PERL) arch/ia64/prefix/apply_unnrv2b_prefix.pl $(BIN)/unnrv2b $< > $@ + +# Placeholder; add no extra symbols to %.sym +$(BIN)/%.zsym: $(BIN)/%.sym $(MAKEDEPS) + cp -f $< $@ + +# rules to generate efi loadable image +SUFFIXES += efi zefi +$(BIN)/efi_prefix.tmp: $(BIN)/efi_prefix.o arch/ia64/prefix/efi_prefix.lds $(MAKEDEPS) + $(LD) -T arch/ia64/prefix/efi_prefix.lds $< -o $@ + +$(BIN)/efi_prefix: $(BIN)/efi_prefix.tmp $(MAKEDEPS) + $(OBJCOPY) -O binary $< $@ + +$(BIN)/%.efi: $(BIN)/%.img $(BIN)/%.tmp $(BIN)/efi_prefix arch/ia64/prefix/apply_efi_prefix.pl $(MAKEDEPS) + @$(SIZE) $(BIN)/$(*).tmp | (read l1; read d1 d2 bss rest ; echo $$bss ) + $(PERL) arch/ia64/prefix/apply_efi_prefix.pl $(BIN)/efi_prefix $< `$(SIZE) $(BIN)/$(*).tmp | (read l1; read d1 d2 bss rest ; echo $$bss )` > $@ + +$(BIN)/%.zefi: $(BIN)/%.zimg $(BIN)/%.tmp $(BIN)/efi_prefix arch/ia64/prefix/apply_efi_prefix.pl $(MAKEDEPS) + @$(SIZE) $(BIN)/$(*).tmp | (read l1; read d1 d2 d3 size rest ; echo $$size ) + $(PERL) arch/ia64/prefix/apply_efi_prefix.pl $(BIN)/efi_prefix $< `$(SIZE) $(BIN)/$(*).tmp | (read l1; read d1 d2 d3 size rest ; echo $$size )` > $@ + diff --git a/src/arch/ia64/core/__call.S b/src/arch/ia64/core/__call.S new file mode 100644 index 00000000..a11b8264 --- /dev/null +++ b/src/arch/ia64/core/__call.S @@ -0,0 +1,68 @@ + /* Trampoline for calling outside of etherboot */ + + .text + .globl __call + .proc __call +__call: + alloc loc0=ar.pfs,8,3,8,0 /* in, local, out, rotating */ + mov loc1=rp + mov loc2=gp + ld8 r14=[in0],8 + ;; + ld8 gp=[in0] + mov r28=in1 /* So we can use stacked pal calling conventions */ + mov out0=in1 + mov out1=in2 + mov out2=in3 + mov out3=in4 + mov out4=in5 + mov out5=in6 + mov out6=in7 + mov out7=0 /* So we can work with sal calling conventions */ + + mov b6=r14 + ;; + br.call.sptk.few rp=b6 + ;; + rsm psr.i /* disable interrupts */ + ;; + mov gp=loc2 + mov rp=loc1 + ;; + mov ar.pfs=loc0 + br.ret.sptk.many rp + + .size __call, . - __call + .endp __call + + + .text + .globl pal_call + .proc pal_call +pal_call: + alloc loc0 = ar.pfs,4,3,0,0 /* in, local, out, rotating */ + mov loc1 = rp + mov loc2 = gp + add r8 = @gprel(pal_entry),gp + add r9 = @gprel(pal_ret),gp + ;; + ld8 r14 = [r8] + ;; + mov r28 = in0 + mov r29 = in1 + mov r30 = in2 + mov r31 = in3 + mov b6 = r14 + mov rp = r9 + rsm psr.i /* disable interrupts */ + ;; + br.sptk.few b6 + ;; +pal_ret: + rsm psr.i /* disable interrupts */ + ;; + mov gp=loc2 + mov rp=loc1 + ;; + mov ar.pfs=loc0 + br.ret.sptk.many rp diff --git a/src/arch/ia64/core/efi.c b/src/arch/ia64/core/efi.c new file mode 100644 index 00000000..97e585fc --- /dev/null +++ b/src/arch/ia64/core/efi.c @@ -0,0 +1,1026 @@ +#include "efi/efi.h" +#include "etherboot.h" +#include "elf.h" +#include "sal.h" +#include "pal.h" + +#warning "Place a declaration of lookup_efi_nic somewhere useful" +EFI_NETWORK_INTERFACE_IDENTIFIER_INTERFACE *lookup_efi_nic(int index); + +#warning "Place the declaraction of __call someplace more appropriate\n" +extern EFI_STATUS __call(void *,...); + +/* Keep 16M free in case EFI needs to allocate some memory. + * In the worst case this is only 1/8 the memory on an Itanium. + */ +#define EFI_RESERVE_LOW_PAGES ((8*1024*1024)/EFI_PAGE_SIZE) +#define EFI_RESERVE_HIGH_PAGES ((8*1024*1024)/EFI_PAGE_SIZE) + +struct console_info { + uint16_t num_cols; + uint16_t num_rows; + uint16_t orig_x; + uint16_t orig_y; +}; + +struct efi_mem_map { + uint64_t map_size; + uint64_t map_key; + uint64_t descriptor_size; + uint32_t descriptor_version; + uint32_t pad; + EFI_MEMORY_DESCRIPTOR map[64]; +}; + +struct efi_info { + int flags; +#define READ_SYSTAB 1 +#define READ_FPSWA 2 +#define READ_MEMMAP 4 +#define READ_CONINFO 8 + EFI_SYSTEM_TABLE *systab; + void *fpswa; + struct efi_mem_map mem_map; + struct console_info coninfo; +}; + + +unsigned long io_base; + +/* local globals */ +static struct efi_info efi_info; +static EFI_HANDLE etherboot_handle; +static EFI_BOOT_SERVICES *boot_services; +static SIMPLE_TEXT_OUTPUT_INTERFACE *conout; +static SIMPLE_INPUT_INTERFACE *conin; +static void *mps_table; +static void *acpi20_table; +static void *smbios_table; +static void *nii_table; + +/* local functions */ + +static EFI_STATUS efi_locate_handle( + EFI_LOCATE_SEARCH_TYPE search_type, + EFI_GUID *protocol, void *search_key, + UINTN *buffer_size, EFI_HANDLE *buffer) +{ + if (!boot_services) + return EFI_NOT_FOUND; + return __call(boot_services->LocateHandle, + search_type, protocol, search_key, buffer_size, buffer); +} + +static EFI_STATUS efi_handle_protocol(EFI_HANDLE handle, EFI_GUID *protocol, void **interface) +{ + if (!boot_services) + return EFI_UNSUPPORTED; + return __call(boot_services->HandleProtocol, handle, protocol, interface); +} + +static EFI_STATUS efi_locate_device_path(EFI_GUID *protocol, EFI_DEVICE_PATH **device_path, + EFI_HANDLE *device) +{ + if (!boot_services) + return EFI_NOT_FOUND; + return __call(boot_services->LocateDevicePath, protocol, device_path, device); +} + +static EFI_STATUS efi_allocate_pages(EFI_ALLOCATE_TYPE type, EFI_MEMORY_TYPE memory_type, + UINTN pages, EFI_PHYSICAL_ADDRESS *memory) +{ + if (!boot_services) + return EFI_OUT_OF_RESOURCES; + return __call(boot_services->AllocatePages, + type, memory_type, pages, memory); +} + +static EFI_STATUS efi_free_pages(EFI_PHYSICAL_ADDRESS memory, UINTN pages) +{ + if(pages == 0) + return EFI_SUCCESS; + if (!boot_services) + return EFI_INVALID_PARAMETER; + return __call(boot_services->FreePages, memory, pages); +} + +static EFI_STATUS efi_get_memory_map(UINTN *map_size, EFI_MEMORY_DESCRIPTOR *map, + UINTN *map_key, UINTN *descriptor_size, UINT32 *descriptor_version) +{ + if (!boot_services) + return EFI_INVALID_PARAMETER; + return __call(boot_services->GetMemoryMap, + map_size, map, map_key, descriptor_size, descriptor_version); +} + +static EFI_STATUS efi_free_pool(void *buffer) +{ + if (!boot_services) + return EFI_INVALID_PARAMETER; + return __call(boot_services->FreePool, buffer); +} +static EFI_STATUS efi_stall(UINTN microseconds) +{ + if (!boot_services) + return EFI_UNSUPPORTED; + return __call(boot_services->Stall, microseconds); +} + +static EFI_STATUS efi_set_watchdog_timer( + UINTN timeout, UINT64 watchdog_code, UINTN data_size, CHAR16 *watchdog_data) +{ + if (!boot_services) + return EFI_UNSUPPORTED; + return __call(boot_services->SetWatchdogTimer, + timeout, watchdog_code, data_size, watchdog_data); +} + + +static void efi_exit_boot_services(struct efi_mem_map *map) +{ + EFI_STATUS status; + if (!boot_services) + return; + status = __call(boot_services->ExitBootServices, + etherboot_handle, map->map_key); + if (status != EFI_SUCCESS) { + printf("ExitBootServices failed: %lx\n", status); + } + conout = 0; + conin = 0; + boot_services = 0; +} + +static void efi_free_memory(struct efi_mem_map *map) +{ + EFI_MEMORY_DESCRIPTOR *desc, *tail; +#define next_desc(desc, size) ((EFI_MEMORY_DESCRIPTOR *)(((char *)(desc)) + (size))) + tail = next_desc(map->map, map->map_size); + for(desc = map->map; desc < tail; desc = next_desc(desc, map->descriptor_size)) { + EFI_STATUS status; + EFI_PHYSICAL_ADDRESS start, end; + UINTN pages; + int may_free; + + start = desc->PhysicalStart; + pages = desc->NumberOfPages; + end = start + pages * EFI_PAGE_SIZE; + + + may_free = 0; + /* The only canidates are Loader Code and Data */ + if ((desc->Type == EfiLoaderData) || + (desc->Type == EfiLoaderCode)) + may_free = 1; + + /* Don't free anything etherboot lives in */ + if ((may_free) && + (start < virt_to_phys(_end)) && + (end > virt_to_phys(_text))) + may_free = 0; + + /* Continue if it is not memory we want to free */ + if (!may_free) + continue; + + status = efi_free_pages(start, pages); + if (status != EFI_SUCCESS) { + printf("free_pages: %lx\n", status); + } + } +#undef next_desc +} + +static void read_efi_mem_map(struct efi_mem_map *map) +{ + EFI_STATUS status; + map->map_size = sizeof(map->map); + status = efi_get_memory_map( + &map->map_size, map->map, &map->map_key, + &map->descriptor_size, &map->descriptor_version); + if (status != EFI_SUCCESS) { + printf("read_efi_mem_map failed: %lx\n", status); + map->map_size = 0; + } + /* map->descriptor_size should only grow larger */ + /* map->descriptor_version should only increase and retain + * a backward compatible format. + */ +} + +#if 0 +static const char *efi_mem_type_name(uint32_t type) +{ + const char *type_name; + if (type == EfiReservedMemoryType) + type_name = "EfiReservedMemoryType "; + else if (type == EfiLoaderCode) + type_name = "EfiLoaderCode "; + else if (type == EfiLoaderData) + type_name = "EfiLoaderData "; + else if (type == EfiBootServicesCode) + type_name = "EfiBootServicesCode "; + else if (type == EfiBootServicesData) + type_name = "EfiBootServicesData "; + else if (type == EfiRuntimeServicesCode) + type_name = "EfiRuntimeServicesCode "; + else if (type == EfiRuntimeServicesData) + type_name = "EfiRuntimeServicesData "; + else if (type == EfiConventionalMemory) + type_name = "EfiConventionalMemory "; + else if (type == EfiUnusableMemory) + type_name = "EfiUnusableMemory "; + else if (type == EfiACPIReclaimMemory) + type_name = "EfiACPIReclaimMemory "; + else if (type == EfiACPIMemoryNVS) + type_name = "EfiACPIMemoryNVS "; + else if (type == EfiMemoryMappedIO) + type_name = "EfiMemoryMappedIO "; + else if (type == EfiMemoryMappedIOPortSpace) + type_name = "EfiMemoryMappedIOPortSpace"; + else if (type == EfiPalCode) + type_name = "EfiPalCode "; + else + type_name = "???? "; + return type_name; +} + +static void print_efi_mem_map(struct efi_mem_map *map) +{ + EFI_MEMORY_DESCRIPTOR *desc, *end; +#define next_desc(desc, size) ((EFI_MEMORY_DESCRIPTOR *)(((char *)(desc)) + (size))) + end = next_desc(map->map, map->map_size); + for(desc = map->map; desc < end ; desc = next_desc(desc, map->descriptor_size)) { + const char *mem_type; + unsigned long start, end, virt, virt_end; + uint64_t attr; + mem_type = efi_mem_type_name(desc->Type); + start = desc->PhysicalStart; + end = start + desc->NumberOfPages*EFI_PAGE_SIZE; + virt = desc->VirtualStart; + virt_end = virt + desc->NumberOfPages*EFI_PAGE_SIZE; + attr = desc->Attribute; + printf( "mem: %hhx %s @ %#lx-%#lx", + desc->Type, mem_type, start, end); + if (attr & EFI_MEMORY_UC) + printf("UC "); + if (attr & EFI_MEMORY_WC) + printf("WC "); + if (attr & EFI_MEMORY_WT) + printf("WT "); + if (attr & EFI_MEMORY_WB) + printf("WB "); + if (attr & EFI_MEMORY_UCE) + printf("UCE "); + + if (attr & EFI_MEMORY_WP) + printf("WP "); + if (attr & EFI_MEMORY_RP) + printf("RP "); + if (attr & EFI_MEMORY_XP) + printf("XP "); + + if (attr & EFI_MEMORY_RUNTIME) + printf("RUNTIME "); + + printf("\n"); + } +#undef next_desc +} +#endif + +static void efi_allocate_memory(struct efi_mem_map *map) +{ + EFI_MEMORY_DESCRIPTOR *desc, *end; + unsigned long low_free, high_free; + +#define next_desc(desc, size) ((EFI_MEMORY_DESCRIPTOR *)(((char *)(desc)) + (size))) + end = next_desc(map->map, map->map_size); + /* Find out how much memory is free */ + low_free = high_free = 0; + for(desc = map->map; desc < end ; desc = next_desc(desc, map->descriptor_size)) { + unsigned long start, middle, end; + if (desc->Type != EfiConventionalMemory) + continue; + start = desc->PhysicalStart; + end = desc->PhysicalStart + (desc->NumberOfPages*EFI_PAGE_SIZE); + if (start < 0x100000000UL) { + if (end > 0x100000000UL) { + middle = 0x10000000UL; + } else { + middle = end; + } + } else { + middle = start; + } + + low_free += (middle - start)/EFI_PAGE_SIZE; + high_free += (end - middle)/EFI_PAGE_SIZE; + } + /* O.k. Now allocate all of the conventional memory, reserving only a tiny + * fraction for efi. + */ + for(desc = map->map; desc < end ; desc = next_desc(desc, map->descriptor_size)) { + EFI_STATUS status; + EFI_PHYSICAL_ADDRESS address; + UINTN pages; + unsigned long start, middle, end; + unsigned long low_pages, high_pages; + if (desc->Type != EfiConventionalMemory) + continue; + start = desc->PhysicalStart; + end = desc->PhysicalStart + (desc->NumberOfPages*EFI_PAGE_SIZE); + if (start < 0x100000000UL) { + if (end > 0x100000000UL) { + middle = 0x10000000UL; + } else { + middle = end; + } + } else { + middle = start; + } + low_pages = (middle - start)/EFI_PAGE_SIZE; + high_pages = (end - middle)/EFI_PAGE_SIZE; + if (low_pages && (low_free > EFI_RESERVE_LOW_PAGES)) { + address = start; + pages = low_pages; + if ((low_free - pages) < EFI_RESERVE_LOW_PAGES) { + pages = low_free - EFI_RESERVE_LOW_PAGES; + } + status = efi_allocate_pages( + AllocateAddress, EfiLoaderData, pages, &address); + if (status != EFI_SUCCESS) { + printf("allocate_pages @%lx for %ld pages failed: %ld\n", + desc->PhysicalStart, pages, status); + } + low_free -= pages; + } + if (high_pages && (high_free > EFI_RESERVE_HIGH_PAGES)) { + address = middle; + pages = high_pages; + if ((high_free - pages) < EFI_RESERVE_HIGH_PAGES) { + pages = high_free - EFI_RESERVE_HIGH_PAGES; + } + status = efi_allocate_pages( + AllocateAddress, EfiLoaderData, pages, &address); + if (status != EFI_SUCCESS) { + printf("allocate_pages @%lx for %ld pages failed: %ld\n", + desc->PhysicalStart, pages, status); + } + high_free -= pages; + } + } +#undef next_desc +} + +static void set_io_base(struct efi_mem_map *map) +{ + EFI_MEMORY_DESCRIPTOR *desc, *end; + + io_base = ia64_get_kr0(); /* Default to ar.kr0 */ + +#define next_desc(desc, size) ((EFI_MEMORY_DESCRIPTOR *)(((char *)(desc)) + (size))) + end = next_desc(map->map, map->map_size); + + for(desc = map->map; desc < end ; desc = next_desc(desc, map->descriptor_size)) { + if (desc->Type == EfiMemoryMappedIOPortSpace) { + io_base = desc->PhysicalStart; + break; + } + } +#undef next_desc +} + +#define MAX_EFI_DEVICES 32 +static void efi_stop_nics(void) +{ + static EFI_GUID simple_net_protocol = EFI_SIMPLE_NETWORK_PROTOCOL; + EFI_SIMPLE_NETWORK *simple; + EFI_STATUS status; + EFI_HANDLE handles[MAX_EFI_DEVICES]; + EFI_HANDLE handle; + UINTN devices; + unsigned i; + + if (!boot_services) + return; + + devices = sizeof(handles); + status = efi_locate_handle( + ByProtocol, &simple_net_protocol, 0, &devices, handles); + if (status != EFI_SUCCESS) + return; + devices /= sizeof(handles[0]); + for(i = 0; i < devices; i++) { + void *that; + handle = handles[i]; + status = efi_handle_protocol(handle, &simple_net_protocol, &that); + if (status != EFI_SUCCESS) + continue; + simple = that; + if ((simple->Mode->State == EfiSimpleNetworkInitialized)) { + status = __call(simple->Shutdown, simple); + status = __call(simple->Stop, simple); + } + else if (simple->Mode->State == EfiSimpleNetworkStarted) { + status = __call(simple->Stop, simple); + } + } +} + +static void efi_get_coninfo(struct console_info *info) +{ + EFI_STATUS status; + UINTN cols, rows; + + /* Initialize with some silly safe values */ + info->num_cols = 80; + info->num_rows = 24; + info->orig_x = 0; + info->orig_y = 0; + + status = EFI_UNSUPPORTED; + if (conout) { + status = __call(conout->QueryMode, conout, conout->Mode->Mode, &cols, &rows); + if (status) { + printf("QueryMode Failed cannout get console parameters: %ld\n", status); + } else { + info->num_cols = cols; + info->num_rows = rows; + info->orig_x = conout->Mode->CursorColumn; + info->orig_y = conout->Mode->CursorRow; + } + } +} + +static void *efi_get_fpswa(void) +{ + static EFI_GUID fpswa_protocol = FPSWA_PROTOCOL; + EFI_STATUS status; + EFI_HANDLE fpswa_handle; + UINTN devices; + void *result; + + /* The FPSWA is the Floating Point Software Assist driver, + * to some extent it makes sense but it has one large flaw. + * It fails to install an EFI Configuration table, so the + * OS does not need assistance from the bootloader to find it. + */ + devices = sizeof(fpswa_handle); + status = efi_locate_handle( + ByProtocol, &fpswa_protocol, 0, &devices, &fpswa_handle); + if (status != EFI_SUCCESS) + return 0; + + status = efi_handle_protocol( + fpswa_handle, &fpswa_protocol, &result); + if (status != EFI_SUCCESS) + return 0; + + return result; +} + + +/* Exported functions */ + + +void arch_main(in_call_data_t *data, va_list params) +{ + EFI_STATUS status; + unsigned char *note, *end; + + /* IA64 doesn't have an in_call() implementation; start.S + * passes in this parameter directly on the stack instead of + * as part of the in_call_data_t structure or the parameter + * list. params is unusable: don't attempt to access it. + */ + struct Elf_Bhdr *ptr = (struct Elf_Bhdr *)data; + + memset(&efi_info, 0, sizeof(efi_info)); + note = ((char *)bhdr) + sizeof(*bhdr); + end = ((char *)bhdr) + bhdr->b_size; + if (bhdr->b_signature != 0x0E1FB007) { + printf("Bad bhdr(%lx) signature(%x)!\n", + (unsigned long) bhdr, bhdr->b_signature); + note = end = 0; + } + while(note < end) { + Elf_Nhdr *hdr; + unsigned char *n_name, *n_desc, *next; + hdr = (Elf_Nhdr *)note; + n_name = note + sizeof(*hdr); + n_desc = n_name + ((hdr->n_namesz + 3) & ~3); + next = n_desc + ((hdr->n_descsz + 3) & ~3); + if (next > end) + break; +#if 0 + printf("n_type: %x n_name(%d): n_desc(%d): \n", + hdr->n_type, hdr->n_namesz, hdr->n_descsz); +#endif + if ((hdr->n_namesz == 10) && + (memcmp(n_name, "Etherboot", 10) == 0)) { + switch(hdr->n_type) { + case EB_IA64_IMAGE_HANDLE: + { + uint64_t *handlep = (void *)n_desc; + etherboot_handle = (EFI_HANDLE)(*handlep); + break; + } + case EB_IA64_SYSTAB: + { + uint64_t *systabp = (void *)n_desc; + efi_info.systab = (void *)(*systabp); + efi_info.flags |= READ_SYSTAB; + break; + } + case EB_IA64_FPSWA: + { + uint64_t*fpswap = (void *)n_desc; + efi_info.fpswa = (void *)(*fpswap); + efi_info.flags |= READ_FPSWA; + break; + } + case EB_IA64_CONINFO: + { + struct console_info *coninfop = (void *)n_desc; + efi_info.coninfo = *coninfop; + efi_info.flags |= READ_CONINFO; + break; + } + case EB_IA64_MEMMAP: + { + struct efi_mem_map *mem_mapp = (void *)n_desc; + efi_info.mem_map = *mem_mapp; + efi_info.flags |= READ_MEMMAP; + break; + } + default: + break; + } + } + note = next; + } + if (!(efi_info.flags & READ_SYSTAB)) { + printf("No EFI systab\n"); + return; + } + + /* If I have an efi memory map assume ExitBootServices has been called. + */ +#warning "FIXME see if there is a better test for boot services still being active " + printf("FIXME Develop a better test for boot services still being active\n"); + if (!(efi_info.flags & READ_MEMMAP)) { + conout = efi_info.systab->ConOut; + conin = efi_info.systab->ConIn; + boot_services = efi_info.systab->BootServices; + } + + if (!(efi_info.flags & READ_CONINFO)) { + efi_info.flags |= READ_CONINFO; + efi_get_coninfo(&efi_info.coninfo); + } + if (!(efi_info.flags & READ_FPSWA)) { + efi_info.flags |= READ_FPSWA; + efi_info.fpswa = efi_get_fpswa(); + } + if (!(efi_info.flags & READ_MEMMAP)) { + efi_info.flags |= READ_MEMMAP; + read_efi_mem_map(&efi_info.mem_map); + /* Allocate all of the memory efi can spare */ + efi_allocate_memory(&efi_info.mem_map); + /* Now refresh the memory map */ + read_efi_mem_map(&efi_info.mem_map); + } + /* Get the io_base for legacy i/o */ + set_io_base(&efi_info.mem_map); + + /* Attempt to disable the watchdog timer.. + * Nothing useful can be done if this fails, so ignore the return code. + */ + status = efi_set_watchdog_timer(0, 1, 0, 0); + + /* Shutdown efi network drivers so efi doesn't get too confused */ + efi_stop_nics(); + + if (efi_info.systab) { + static const EFI_GUID mps_table_guid = MPS_TABLE_GUID; + static const EFI_GUID acpi20_table_guid = ACPI_20_TABLE_GUID; + static const EFI_GUID smbios_table_guid = SMBIOS_TABLE_GUID; + static const EFI_GUID sal_system_table_guid = SAL_SYSTEM_TABLE_GUID; + static const EFI_GUID nii_table_guid = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL; + EFI_SYSTEM_TABLE *systab; + unsigned i; + systab = efi_info.systab; + for(i = 0; i < systab->NumberOfTableEntries; i++) { + EFI_GUID *guid; + void *table = systab->ConfigurationTable[i].VendorTable; + guid = &systab->ConfigurationTable[i].VendorGuid; + +#if 0 + printf("GUID: %x-%hx-%hx-%hhx-%hhx-%hhx-%hhx-%hhx-%hhx-%hhx-%hhx Table: %lx\n", + guid->Data1, guid->Data2, guid->Data3, + guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3], + guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7], + table); +#endif + + if (memcmp(guid, &mps_table_guid, 16) == 0) { + mps_table = table; + } + if (memcmp(guid, &acpi20_table_guid, 16) == 0) { + acpi20_table = table; + } + if (memcmp(guid, &smbios_table_guid, 16) == 0) { + smbios_table = table; + } + if (memcmp(guid, &sal_system_table_guid, 16) == 0) { + parse_sal_system_table(table); + } + if (memcmp(guid, &nii_table_guid, 16) == 0) { + nii_table = table; + } + } + } +} + +void arch_on_exit(int status __unused) +{ + if (!boot_services) + return; + read_efi_mem_map(&efi_info.mem_map); + efi_free_memory(&efi_info.mem_map); +} + +void arch_relocate_to(unsigned long addr) +{ + EFI_PHYSICAL_ADDRESS address, end; + UINTN pages; + EFI_STATUS status; + + if (!boot_services) + return; + + /* Find the efi pages where the new etherboot will sit */ + address = addr & ~(EFI_PAGE_SIZE -1); + end = (addr + (_end - _text) + EFI_PAGE_SIZE -1) & ~EFI_PAGE_SIZE; + pages = (end - address)/EFI_PAGE_SIZE; + + /* Reallocate the memory for the new copy of etherboot as LoaderCode */ + status = efi_free_pages(address, pages); + if (status != EFI_SUCCESS) { + printf("efi_free_pages failed!: %lx\n", status); + return; + } + status = efi_allocate_pages(AllocateAddress, EfiLoaderCode, pages, &address); + if (status != EFI_SUCCESS) { + printf("efi_allocate_pages failed! %lx\n", status); + return; + } +} + + +struct meminfo meminfo; +void get_memsizes(void) +{ + EFI_MEMORY_DESCRIPTOR *desc, *end; + struct efi_mem_map *map; +#define next_desc(desc, size) ((EFI_MEMORY_DESCRIPTOR *)(((char *)(desc)) + (size))) + + map = &efi_info.mem_map; + end = next_desc(map->map, map->map_size); + + meminfo.map_count = 0; + for(desc = map->map; desc < end ; desc = next_desc(desc, map->descriptor_size)) { + uint64_t start, size, end; + unsigned long mem_k; + + start = desc->PhysicalStart; + size = desc->NumberOfPages*EFI_PAGE_SIZE; + end = start + size; + + if ((desc->Type != EfiLoaderCode) && + (desc->Type != EfiLoaderData)) { + continue; + } + + meminfo.map[meminfo.map_count].addr = start; + meminfo.map[meminfo.map_count].size = size; + meminfo.map[meminfo.map_count].type = E820_RAM; + meminfo.map_count++; + + end >>= 10; + mem_k = end; + if (end & 0xFFFFFFFF00000000ULL) { + mem_k = 0xFFFFFFFF; + } + /* Set the base basememsize */ + if ((mem_k <= 640) && (meminfo.basememsize <= mem_k)) { + meminfo.basememsize = mem_k; + } + /* Set the total memsize */ + if ((mem_k >= 1024) && (meminfo.memsize <= (mem_k - 1024))) { + meminfo.memsize = mem_k - 1024; + } + if (meminfo.map_count == E820MAX) + break; + } +#undef next_desc +} + + +EFI_NETWORK_INTERFACE_IDENTIFIER_INTERFACE *lookup_efi_nic(int index) +{ + static EFI_GUID protocol = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL; + EFI_HANDLE handles[MAX_EFI_DEVICES]; + EFI_STATUS status; + UINTN devices; + void *that; + + if (!boot_services) + return 0; + if (index < 0) { + return 0; + } + devices = sizeof(handles); + status = efi_locate_handle( + ByProtocol, &protocol, 0, &devices, handles); + if (status != EFI_SUCCESS) + return 0; + devices /= sizeof(handles[0]); + if (index >= devices) + return 0; + status = efi_handle_protocol(handles[index], &protocol, &that); + if (status != EFI_SUCCESS) + return 0; + return that; +} + +#if defined(CONSOLE_FIRMWARE) +void console_putc(int c) +{ + CHAR16 str[2]; + if (!conout) + return; + str[0] = c; + str[1] = 0; + __call(conout->OutputString, conout, str); +} + +static int efi_have_key = 0; +static int efi_key; +int console_ischar(void) +{ + EFI_STATUS status; + EFI_INPUT_KEY new_key; + if (!conin) + return 0; + if (efi_have_key) { + return 1; + } + status = __call(conin->ReadKeyStroke, conin, &new_key); + if (status == EFI_SUCCESS) { + if ((new_key.UnicodeChar >= 0) && (new_key.UnicodeChar < 0x7f)) { + efi_have_key = 1; + efi_key = new_key.UnicodeChar; + } + else if (new_key.ScanCode == 0x17) { + efi_have_key = 1; + efi_key = K_ESC; + } + } + return efi_have_key; +} + +int console_getc(void) +{ + if (efi_have_key) { + efi_have_key = 0; + } + + return efi_key; +} +#endif /* CONSOLE_FIRMWARE */ + +#define NAME "Etherboot" +#define FIRMWARE "EFI" + +#define SZ(X) ((sizeof(X)+3) & ~3) +#define CP(D,S) (memcpy(&(D), &(S), sizeof(S))) + + +struct elf_notes { + /* CAREFUL this structure is carefully arranged to avoid + * alignment problems. + */ + /* The note header */ + struct Elf_Bhdr hdr; + + /* First the Fixed sized entries that must be well aligned */ + + /* Insert a nop record so the next record is 64bit aligned */ + Elf_Nhdr nf0; + + /* Pointer to bootp data */ + Elf_Nhdr nf1; + char nf1_name[SZ(EB_PARAM_NOTE)]; + uint64_t nf1_bootp_data; + + /* Pointer to ELF header */ + Elf_Nhdr nf2; + char nf2_name[SZ(EB_PARAM_NOTE)]; + uint64_t nf2_header; + + /* The EFI systab pointer */ + Elf_Nhdr nf3; + char nf3_name[SZ(EB_PARAM_NOTE)]; + uint64_t nf3_systab; + + /* The FPSWA pointer */ + Elf_Nhdr nf4; + char nf4_name[SZ(EB_PARAM_NOTE)]; + uint64_t nf4_fpswa; + + /* The memory map */ + Elf_Nhdr nf5; + char nf5_name[SZ(EB_PARAM_NOTE)]; + struct efi_mem_map nf5_map; + + /* The console info, silly but elilo passes it... */ + Elf_Nhdr nf6; + char nf6_name[SZ(EB_PARAM_NOTE)]; + struct console_info nf6_coninfo; + + /* Then the variable sized data string data where alignment does not matter */ + + /* The bootloader name */ + Elf_Nhdr nv1; + char nv1_desc[SZ(NAME)]; + /* The bootloader version */ + Elf_Nhdr nv2; + char nv2_desc[SZ(VERSION)]; + /* The firmware type */ + Elf_Nhdr nv3; + char nv3_desc[SZ(FIRMWARE)]; + /* Name of the loaded image */ + Elf_Nhdr nv4; + char nv4_loaded_image[128]; + /* An empty command line */ + Elf_Nhdr nv5; + char nv5_cmdline[SZ("")]; +}; + +#define ELF_NOTE_COUNT (6+5) + +static struct elf_notes notes; +struct Elf_Bhdr *prepare_boot_params(void *header) +{ + /* Shutdown the boot services */ + if (boot_services) { + efi_get_coninfo(&efi_info.coninfo); + read_efi_mem_map(&efi_info.mem_map); + efi_exit_boot_services(&efi_info.mem_map); + } + + memset(¬es, 0, sizeof(notes)); + notes.hdr.b_signature = 0x0E1FB007; + notes.hdr.b_size = sizeof(notes); + notes.hdr.b_checksum = 0; + notes.hdr.b_records = ELF_NOTE_COUNT; + + /* Initialize the fixed length entries. */ + + /* Align the fixed length entries to a 64bit boundary */ + notes.nf0.n_namesz = 0; + notes.nf0.n_descsz = 0; + notes.nf0.n_type = EBN_NOP; + + notes.nf1.n_namesz = sizeof(EB_PARAM_NOTE); + notes.nf1.n_descsz = sizeof(notes.nf1_bootp_data); + notes.nf1.n_type = EB_BOOTP_DATA; + CP(notes.nf1_name, EB_PARAM_NOTE); + notes.nf1_bootp_data = virt_to_phys(BOOTP_DATA_ADDR); + + notes.nf2.n_namesz = sizeof(EB_PARAM_NOTE); + notes.nf2.n_descsz = sizeof(notes.nf2_header); + notes.nf2.n_type = EB_HEADER; + CP(notes.nf2_name, EB_PARAM_NOTE); + notes.nf2_header = virt_to_phys(header); + + notes.nf3.n_namesz = sizeof(EB_PARAM_NOTE); + notes.nf3.n_descsz = sizeof(notes.nf3_systab); + notes.nf3.n_type = EB_IA64_SYSTAB; + CP(notes.nf3_name, EB_PARAM_NOTE); + notes.nf3_systab = (unsigned long)efi_info.systab; + + notes.nf4.n_namesz = sizeof(EB_PARAM_NOTE); + notes.nf4.n_descsz = sizeof(notes.nf4_fpswa); + notes.nf4.n_type = EB_IA64_FPSWA; + CP(notes.nf4_name, EB_PARAM_NOTE); + notes.nf4_fpswa = (unsigned long)efi_info.fpswa; + + notes.nf5.n_namesz = sizeof(EB_PARAM_NOTE); + notes.nf5.n_descsz = sizeof(notes.nf5_map); + notes.nf5.n_type = EB_IA64_MEMMAP; + CP(notes.nf5_name, EB_PARAM_NOTE); + notes.nf5_map = efi_info.mem_map; + + notes.nf6.n_namesz = sizeof(EB_PARAM_NOTE); + notes.nf6.n_descsz = sizeof(notes.nf6_coninfo); + notes.nf6.n_type = EB_IA64_CONINFO; + CP(notes.nf6_name, EB_PARAM_NOTE); + notes.nf6_coninfo = efi_info.coninfo; + + /* Initialize the variable length entries */ + notes.nv1.n_namesz = 0; + notes.nv1.n_descsz = sizeof(NAME); + notes.nv1.n_type = EBN_BOOTLOADER_NAME; + CP(notes.nv1_desc, NAME); + + notes.nv2.n_namesz = 0; + notes.nv2.n_descsz = sizeof(VERSION); + notes.nv2.n_type = EBN_BOOTLOADER_VERSION; + CP(notes.nv2_desc, VERSION); + + notes.nv3.n_namesz = 0; + notes.nv3.n_descsz = sizeof(FIRMWARE); + notes.nv3.n_type = EBN_FIRMWARE_TYPE; + CP(notes.nv3_desc, FIRMWARE); + + /* Attempt to pass the name of the loaded image */ + notes.nv4.n_namesz = 0; + notes.nv4.n_descsz = sizeof(notes.nv4_loaded_image); + notes.nv4.n_type = EBN_LOADED_IMAGE; + memcpy(¬es.nv4_loaded_image, KERNEL_BUF, sizeof(notes.nv4_loaded_image)); + + /* Pass an empty command line for now */ + notes.nv5.n_namesz = 0; + notes.nv5.n_descsz = sizeof(""); + notes.nv5.n_type = EBN_COMMAND_LINE; + CP(notes.nv5_cmdline, ""); + + notes.hdr.b_checksum = ipchksum(¬es, sizeof(notes)); + /* Like UDP invert a 0 checksum to show that a checksum is present */ + if (notes.hdr.b_checksum == 0) { + notes.hdr.b_checksum = 0xffff; + } + + return ¬es.hdr; +} + +int elf_start(unsigned long machine __unused, unsigned long entry, unsigned long params) +{ + struct elf_notes *notes; + int result; + /* Since we can do both be polite and also pass the linux + * ia64_boot_param table. + */ + static struct ia64_boot_param { + uint64_t command_line; /* physical address of command line arguments */ + uint64_t efi_systab; /* physical address of EFI system table */ + uint64_t efi_memmap; /* physical address of EFI memory map */ + uint64_t efi_memmap_size; /* size of EFI memory map */ + uint64_t efi_memdesc_size; /* size of an EFI memory map descriptor */ + uint32_t efi_memdesc_version; /* memory descriptor version */ + struct { + uint16_t num_cols; /* number of columns on console output device */ + uint16_t num_rows; /* number of rows on console output device */ + uint16_t orig_x; /* cursor's x position */ + uint16_t orig_y; /* cursor's y position */ + } console_info; + uint64_t fpswa; /* physical address of the fpswa interface */ + uint64_t initrd_start; + uint64_t initrd_size; + } bp; + + notes = phys_to_virt(params); + /* If I don't have notes don't attempt to start the image */ + if (notes == 0) { + return -2; + } + + bp.command_line = (unsigned long)¬es->nv5_cmdline; + bp.efi_systab = notes->nf3_systab; + bp.efi_memmap = (unsigned long)¬es->nf5_map.map; + bp.efi_memmap_size = notes->nf5_map.map_size; + bp.efi_memdesc_size = notes->nf5_map.descriptor_size; + bp.efi_memdesc_version = notes->nf5_map.descriptor_version; + bp.console_info.num_cols = notes->nf6_coninfo.num_cols; + bp.console_info.num_rows = notes->nf6_coninfo.num_rows; + bp.console_info.orig_x = notes->nf6_coninfo.orig_x; + bp.console_info.orig_y = notes->nf6_coninfo.orig_y; + bp.fpswa = notes->nf4_fpswa; + bp.initrd_start = 0; + bp.initrd_size = 0; + + + asm volatile( + ";;\n\t" + "mov r28=%2\n\t" + "mov out0=%3\n\t" + "br.call.sptk.few rp=%1\n\t" + "mov %0=r8\n\t" + : "=r" (result) + : "b"(entry), "r"(&bp),"r"(params) + ); + return result; +} diff --git a/src/arch/ia64/core/etherboot.lds b/src/arch/ia64/core/etherboot.lds new file mode 100644 index 00000000..216cce92 --- /dev/null +++ b/src/arch/ia64/core/etherboot.lds @@ -0,0 +1,82 @@ +OUTPUT_FORMAT("elf64-ia64-little") + +OUTPUT_ARCH(ia64) + +ENTRY(_start) +SECTIONS { + . = 0; + __gp = . + 0x200000; + _virt_start = .; + _text = . ; + .text : { + /* Start address of etherboot in the virtual address space */ + *(.text) + *(.text.*) + _etext = . ; + + _rodata = . ; + . = ALIGN(16); + *(.rodata) + *(.rodata.*) + *(.srodata) + . = ALIGN(16); + pci_drivers = . ; + *(.drivers.pci); + pci_drivers_end = . ; + . = ALIGN(16); + isa_drivers = . ; + *(.drivers.isa); + isa_drivers_end = . ; + . = ALIGN(16); + + . = ALIGN(16); + _rela = . ; + *(.rela.text) + *(.rela.rodata) + *(.rela.drivers.pci) + *(.rela.drivers.isa) + *(.rela.drivers.efi) + *(.rela.data) + *(.rela.sdata) + *(.rela.got) + _erela = . ; + . = ALIGN(16); + _erodata = . ; + } + _rela_size = _erela - _rela ; + .data : { + _data = . ; + *(.data) + *(.got.plt) + *(.got) + *(.sdata) + *(.sbss) + *(.scommon) + *(.data.*) + *(.data1) + . = ALIGN(16); + _edata = . ; + } + _bss = . ; + .bss : { + *(.sbss) + *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + } + _ebss = .; + _end = .; + /DISCARD/ : { + *(.comment) + *(.note) + *(.hash) + *(.dynstr) + *(.dynsym) + *(.IA_64.unwind) + *(.IA_64.unwind_info) + *(.IA64_unwind) + *(.IA64_unwind_info) + *(.dynamic) + } +} diff --git a/src/arch/ia64/core/ia64_timer.c b/src/arch/ia64/core/ia64_timer.c new file mode 100644 index 00000000..3115714a --- /dev/null +++ b/src/arch/ia64/core/ia64_timer.c @@ -0,0 +1,89 @@ +#include "etherboot.h" +#include "timer.h" +#include "sal.h" +#include "pal.h" + +static inline unsigned long get_cycles(void) +{ + unsigned long result; + __asm__ __volatile__(";;mov %0=ar.itc;;" : "=r"(result)); + return result; +} + +/* ------ Calibrate the TSC ------- + * Time how long it takes to excute a loop that runs in known time. + * And find the convertion needed to get to CLOCK_TICK_RATE + */ + +static unsigned long calibrate_cycles(void) +{ + unsigned long platform_ticks_per_second, drift_info; + struct pal_freq_ratio itc_ratio; + long result; + result = sal_freq_base(SAL_FREQ_BASE_PLATFORM, &platform_ticks_per_second, &drift_info); + if (result != 0) { + printf("sal_freq_base failed: %lx\n",result); + exit(1); + } else { + result = pal_freq_ratios(0,0,&itc_ratio); + if (result != 0) { + printf("pal_freq_ratios failed: %lx\n", result); + exit(1); + } + } + /* Avoid division by zero */ + if (itc_ratio.den == 0) + itc_ratio.den = 1; + + return (platform_ticks_per_second *itc_ratio.num)/(itc_ratio.den*TICKS_PER_SEC); +} + +static unsigned long clocks_per_tick; +void setup_timers(void) +{ + if (!clocks_per_tick) { + clocks_per_tick = calibrate_cycles(); + /* Display the CPU Mhz to easily test if the calibration was bad */ + printf("ITC %ld Mhz\n", (clocks_per_tick/1000 * TICKS_PER_SEC)/1000); + } +} + +unsigned long currticks(void) +{ + return get_cycles()/clocks_per_tick; +} + +static unsigned long timer_timeout; +static int __timer_running(void) +{ + return get_cycles() < timer_timeout; +} + +void udelay(unsigned int usecs) +{ + unsigned long now; + now = get_cycles(); + timer_timeout = now + usecs * ((clocks_per_tick * TICKS_PER_SEC)/(1000*1000)); + while(__timer_running()); +} +void ndelay(unsigned int nsecs) +{ + unsigned long now; + now = get_cycles(); + timer_timeout = now + nsecs * ((clocks_per_tick * TICKS_PER_SEC)/(1000*1000*1000)); + while(__timer_running()); +} + +void load_timer2(unsigned int timer2_ticks) +{ + unsigned long now; + unsigned long clocks; + now = get_cycles(); + clocks = timer2_ticks * ((clocks_per_tick * TICKS_PER_SEC)/CLOCK_TICK_RATE); + timer_timeout = now + clocks; +} + +int timer2_running(void) +{ + return __timer_running(); +} diff --git a/src/arch/ia64/core/idiv32.S b/src/arch/ia64/core/idiv32.S new file mode 100644 index 00000000..283eff0e --- /dev/null +++ b/src/arch/ia64/core/idiv32.S @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2000 Hewlett-Packard Co + * Copyright (C) 2000 David Mosberger-Tang + * + * 32-bit integer division. + * + * This code is based on the application note entitled "Divide, Square Root + * and Remainder Algorithms for the IA-64 Architecture". This document + * is available as Intel document number 248725-002 or via the web at + * http://developer.intel.com/software/opensource/numerics/ + * + * For more details on the theory behind these algorithms, see "IA-64 + * and Elementary Functions" by Peter Markstein; HP Professional Books + * (http://www.hp.com/go/retailbooks/) + */ + +#ifdef MODULO +# define OP mod +#else +# define OP div +#endif + +#ifdef UNSIGNED +# define SGN u +# define EXTEND zxt4 +# define INT_TO_FP(a,b) fcvt.xuf.s1 a=b +# define FP_TO_INT(a,b) fcvt.fxu.trunc.s1 a=b +#else +# define SGN +# define EXTEND sxt4 +# define INT_TO_FP(a,b) fcvt.xf a=b +# define FP_TO_INT(a,b) fcvt.fx.trunc.s1 a=b +#endif + +#define PASTE1(a,b) a##b +#define PASTE(a,b) PASTE1(a,b) +#define NAME PASTE(PASTE(__,SGN),PASTE(OP,si3)) + + .text + .global NAME + .proc NAME +NAME : + .regstk 2,0,0,0 + // Transfer inputs to FP registers. + mov r2 = 0xffdd // r2 = -34 + 65535 (fp reg format bias) + EXTEND in0 = in0 // in0 = a + EXTEND in1 = in1 // in1 = b + ;; + setf.sig f8 = in0 + setf.sig f9 = in1 +#ifdef MODULO + sub in1 = r0, in1 // in1 = -b +#endif + ;; + // Convert the inputs to FP, to avoid FP software-assist faults. + INT_TO_FP(f8, f8) + INT_TO_FP(f9, f9) + ;; + setf.exp f7 = r2 // f7 = 2^-34 + frcpa.s1 f6, p6 = f8, f9 // y0 = frcpa(b) + ;; +(p6) fmpy.s1 f8 = f8, f6 // q0 = a*y0 +(p6) fnma.s1 f6 = f9, f6, f1 // e0 = -b*y0 + 1 + ;; +#ifdef MODULO + setf.sig f9 = in1 // f9 = -b +#endif +(p6) fma.s1 f8 = f6, f8, f8 // q1 = e0*q0 + q0 +(p6) fma.s1 f6 = f6, f6, f7 // e1 = e0*e0 + 2^-34 + ;; +#ifdef MODULO + setf.sig f7 = in0 +#endif +(p6) fma.s1 f6 = f6, f8, f8 // q2 = e1*q1 + q1 + ;; + FP_TO_INT(f6, f6) // q = trunc(q2) + ;; +#ifdef MODULO + xma.l f6 = f6, f9, f7 // r = q*(-b) + a + ;; +#endif + getf.sig r8 = f6 // transfer result to result register + br.ret.sptk.many rp + + .size NAME, . - NAME + .endp NAME diff --git a/src/arch/ia64/core/idiv64.S b/src/arch/ia64/core/idiv64.S new file mode 100644 index 00000000..d989a2f5 --- /dev/null +++ b/src/arch/ia64/core/idiv64.S @@ -0,0 +1,96 @@ +/* + * Copyright (C) 1999-2000, 2002 Hewlett-Packard Co + * Copyright (C) 1999-2000 David Mosberger-Tang + * + * 64-bit integer division. + * + * This code is based on the application note entitled "Divide, Square Root + * and Remainder Algorithms for the IA-64 Architecture". This document + * is available as Intel document number 248725-002 or via the web at + * http://developer.intel.com/software/opensource/numerics/ + * + * For more details on the theory behind these algorithms, see "IA-64 + * and Elementary Functions" by Peter Markstein; HP Professional Books + * (http://www.hp.com/go/retailbooks/) + */ + +#ifdef MODULO +# define OP mod +#else +# define OP div +#endif + +#ifdef UNSIGNED +# define SGN u +# define INT_TO_FP(a,b) fcvt.xuf.s1 a=b +# define FP_TO_INT(a,b) fcvt.fxu.trunc.s1 a=b +#else +# define SGN +# define INT_TO_FP(a,b) fcvt.xf a=b +# define FP_TO_INT(a,b) fcvt.fx.trunc.s1 a=b +#endif + +#define PASTE1(a,b) a##b +#define PASTE(a,b) PASTE1(a,b) +#define NAME PASTE(PASTE(__,SGN),PASTE(OP,di3)) + + .text + .global NAME + .proc NAME +NAME : + .prologue + .regstk 2,0,0,0 + // Transfer inputs to FP registers. + setf.sig f8 = in0 + setf.sig f9 = in1 + ;; + .fframe 16 + .save.f 0x20 + stf.spill [sp] = f17,-16 + + // Convert the inputs to FP, to avoid FP software-assist faults. + INT_TO_FP(f8, f8) + ;; + + .save.f 0x10 + stf.spill [sp] = f16 + .body + INT_TO_FP(f9, f9) + ;; + frcpa.s1 f17, p6 = f8, f9 // y0 = frcpa(b) + ;; +(p6) fmpy.s1 f7 = f8, f17 // q0 = a*y0 +(p6) fnma.s1 f6 = f9, f17, f1 // e0 = -b*y0 + 1 + ;; +(p6) fma.s1 f16 = f7, f6, f7 // q1 = q0*e0 + q0 +(p6) fmpy.s1 f7 = f6, f6 // e1 = e0*e0 + ;; +#ifdef MODULO + sub in1 = r0, in1 // in1 = -b +#endif +(p6) fma.s1 f16 = f16, f7, f16 // q2 = q1*e1 + q1 +(p6) fma.s1 f6 = f17, f6, f17 // y1 = y0*e0 + y0 + ;; +(p6) fma.s1 f6 = f6, f7, f6 // y2 = y1*e1 + y1 +(p6) fnma.s1 f7 = f9, f16, f8 // r = -b*q2 + a + ;; +#ifdef MODULO + setf.sig f8 = in0 // f8 = a + setf.sig f9 = in1 // f9 = -b +#endif +(p6) fma.s1 f17 = f7, f6, f16 // q3 = r*y2 + q2 + ;; + .restore sp + ldf.fill f16 = [sp], 16 + FP_TO_INT(f17, f17) // q = trunc(q3) + ;; +#ifdef MODULO + xma.l f17 = f17, f9, f8 // r = q*(-b) + a + ;; +#endif + getf.sig r8 = f17 // transfer result to result register + ldf.fill f17 = [sp] + br.ret.sptk.many rp + + .size NAME, . - NAME + .endp NAME diff --git a/src/arch/ia64/core/longjmp.S b/src/arch/ia64/core/longjmp.S new file mode 100644 index 00000000..d5c8e5ab --- /dev/null +++ b/src/arch/ia64/core/longjmp.S @@ -0,0 +1,163 @@ +/* Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. + Contributed by David Mosberger-Tang . + + The GNU C 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. + + The GNU C 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 the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. + + Note that __sigsetjmp() did NOT flush the register stack. Instead, + we do it here since __longjmp() is usually much less frequently + invoked than __sigsetjmp(). The only difficulty is that __sigsetjmp() + didn't (and wouldn't be able to) save ar.rnat either. This is a problem + because if we're not careful, we could end up loading random NaT bits. + There are two cases: + + (i) ar.bsp < ia64_rse_rnat_addr(jmpbuf.ar_bsp) + ar.rnat contains the desired bits---preserve ar.rnat + across loadrs and write to ar.bspstore + + (ii) ar.bsp >= ia64_rse_rnat_addr(jmpbuf.ar_bsp) + The desired ar.rnat is stored in + ia64_rse_rnat_addr(jmpbuf.ar_bsp). Load those + bits into ar.rnat after setting ar.bspstore. */ + + +# define pPos p6 /* is rotate count positive? */ +# define pNeg p7 /* is rotate count negative? */ + + + /* longjmp(__jmp_buf buf, int val) */ + + .text + .global longjmp + .proc longjmp +longjmp: + + alloc r8=ar.pfs,2,1,0,0 + mov r27=ar.rsc + add r2=0x98,in0 // r2 <- &jmpbuf.orig_jmp_buf_addr + ;; + ld8 r8=[r2],-16 // r8 <- orig_jmp_buf_addr + mov r10=ar.bsp + and r11=~0x3,r27 // clear ar.rsc.mode + ;; + flushrs // flush dirty regs to backing store (must be first in insn grp) + ld8 r23=[r2],8 // r23 <- jmpbuf.ar_bsp + sub r8=r8,in0 // r8 <- &orig_jmpbuf - &jmpbuf + ;; + ld8 r25=[r2] // r25 <- jmpbuf.ar_unat + extr.u r8=r8,3,6 // r8 <- (&orig_jmpbuf - &jmpbuf)/8 & 0x3f + ;; + cmp.lt pNeg,pPos=r8,r0 + mov r2=in0 + ;; +(pPos) mov r16=r8 +(pNeg) add r16=64,r8 +(pPos) sub r17=64,r8 +(pNeg) sub r17=r0,r8 + ;; + mov ar.rsc=r11 // put RSE in enforced lazy mode + shr.u r8=r25,r16 + add r3=8,in0 // r3 <- &jmpbuf.r1 + shl r9=r25,r17 + ;; + or r25=r8,r9 + ;; + mov r26=ar.rnat + mov ar.unat=r25 // setup ar.unat (NaT bits for r1, r4-r7, and r12) + ;; + ld8.fill.nta sp=[r2],16 // r12 (sp) + ld8.fill.nta gp=[r3],16 // r1 (gp) + dep r11=-1,r23,3,6 // r11 <- ia64_rse_rnat_addr(jmpbuf.ar_bsp) + ;; + ld8.nta r16=[r2],16 // caller's unat + ld8.nta r17=[r3],16 // fpsr + ;; + ld8.fill.nta r4=[r2],16 // r4 + ld8.fill.nta r5=[r3],16 // r5 (gp) + cmp.geu p8,p0=r10,r11 // p8 <- (ar.bsp >= jmpbuf.ar_bsp) + ;; + ld8.fill.nta r6=[r2],16 // r6 + ld8.fill.nta r7=[r3],16 // r7 + ;; + mov ar.unat=r16 // restore caller's unat + mov ar.fpsr=r17 // restore fpsr + ;; + ld8.nta r16=[r2],16 // b0 + ld8.nta r17=[r3],16 // b1 + ;; +(p8) ld8 r26=[r11] // r26 <- *ia64_rse_rnat_addr(jmpbuf.ar_bsp) + mov ar.bspstore=r23 // restore ar.bspstore + ;; + ld8.nta r18=[r2],16 // b2 + ld8.nta r19=[r3],16 // b3 + ;; + ld8.nta r20=[r2],16 // b4 + ld8.nta r21=[r3],16 // b5 + ;; + ld8.nta r11=[r2],16 // ar.pfs + ld8.nta r22=[r3],56 // ar.lc + ;; + ld8.nta r24=[r2],32 // pr + mov b0=r16 + ;; + ldf.fill.nta f2=[r2],32 + ldf.fill.nta f3=[r3],32 + mov b1=r17 + ;; + ldf.fill.nta f4=[r2],32 + ldf.fill.nta f5=[r3],32 + mov b2=r18 + ;; + ldf.fill.nta f16=[r2],32 + ldf.fill.nta f17=[r3],32 + mov b3=r19 + ;; + ldf.fill.nta f18=[r2],32 + ldf.fill.nta f19=[r3],32 + mov b4=r20 + ;; + ldf.fill.nta f20=[r2],32 + ldf.fill.nta f21=[r3],32 + mov b5=r21 + ;; + ldf.fill.nta f22=[r2],32 + ldf.fill.nta f23=[r3],32 + mov ar.lc=r22 + ;; + ldf.fill.nta f24=[r2],32 + ldf.fill.nta f25=[r3],32 + cmp.eq p8,p9=0,in1 + ;; + ldf.fill.nta f26=[r2],32 + ldf.fill.nta f27=[r3],32 + mov ar.pfs=r11 + ;; + ldf.fill.nta f28=[r2],32 + ldf.fill.nta f29=[r3],32 + ;; + ldf.fill.nta f30=[r2] + ldf.fill.nta f31=[r3] +(p8) mov r8=1 + + mov ar.rnat=r26 // restore ar.rnat + ;; + mov ar.rsc=r27 // restore ar.rsc +(p9) mov r8=in1 + + invala // virt. -> phys. regnum mapping may change + mov pr=r24,-1 + br.ret.sptk.few b0 + .size longjmp, . - longjmp + .endp longjmp diff --git a/src/arch/ia64/core/memmove.S b/src/arch/ia64/core/memmove.S new file mode 100644 index 00000000..63e093d9 --- /dev/null +++ b/src/arch/ia64/core/memmove.S @@ -0,0 +1,244 @@ +/* Optimized version of the standard memmove() function. + This file is part of the GNU C Library. + Copyright (C) 2000, 2001 Free Software Foundation, Inc. + Contributed by Dan Pop . + + The GNU C 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. + + The GNU C 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 the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* Return: dest + + Inputs: + in0: dest + in1: src + in2: byte count + + The core of the function is the memcpy implementation used in memcpy.S. + When bytes have to be copied backwards, only the easy case, when + all arguments are multiples of 8, is optimised. + + In this form, it assumes little endian mode. For big endian mode, + sh1 must be computed using an extra instruction: sub sh1 = 64, sh1 + or the UM.be bit should be cleared at the beginning and set at the end. */ + + +#define OP_T_THRES 16 +#define OPSIZ 8 + +#define adest r15 +#define saved_pr r17 +#define saved_lc r18 +#define dest r19 +#define src r20 +#define len r21 +#define asrc r22 +#define tmp2 r23 +#define tmp3 r24 +#define tmp4 r25 +#define ptable r26 +#define ploop56 r27 +#define loopaddr r28 +#define sh1 r29 +#define loopcnt r30 +#define value r31 + +#define LOOP(shift) \ + .align 32 ; \ +.loop##shift##: \ +(p[0]) ld8 r[0] = [asrc], 8 ; /* w1 */ \ +(p[MEMLAT+1]) st8 [dest] = value, 8 ; \ +(p[MEMLAT]) shrp value = r[MEMLAT], r[MEMLAT+1], shift ; \ + nop.b 0 ; \ + nop.b 0 ; \ + br.ctop.sptk .loop##shift ; \ + br.cond.sptk .cpyfew ; /* deal with the remaining bytes */ + +#define MEMLAT 21 +#define Nrot (((2*MEMLAT+3) + 7) & ~7) + + .text + .global memmove, memcpy + .proc memove +memcpy: +memmove: + .prologue + alloc r2 = ar.pfs, 3, Nrot - 3, 0, Nrot + .rotr r[MEMLAT + 2], q[MEMLAT + 1] + .rotp p[MEMLAT + 2] + mov ret0 = in0 // return value = dest + .save pr, saved_pr + mov saved_pr = pr // save the predicate registers + .save ar.lc, saved_lc + mov saved_lc = ar.lc // save the loop counter + .body + or tmp3 = in0, in1 ;; // tmp3 = dest | src + or tmp3 = tmp3, in2 // tmp3 = dest | src | len + mov dest = in0 // dest + mov src = in1 // src + mov len = in2 // len + sub tmp2 = r0, in0 // tmp2 = -dest + cmp.eq p6, p0 = in2, r0 // if (len == 0) +(p6) br.cond.spnt .restore_and_exit;;// return dest; + and tmp4 = 7, tmp3 // tmp4 = (dest | src | len) & 7 + cmp.le p6, p0 = dest, src // if dest <= src it's always safe +(p6) br.cond.spnt .forward // to copy forward + add tmp3 = src, len;; + cmp.lt p6, p0 = dest, tmp3 // if dest > src && dest < src + len +(p6) br.cond.spnt .backward // we have to copy backward + +.forward: + shr.u loopcnt = len, 4 ;; // loopcnt = len / 16 + cmp.ne p6, p0 = tmp4, r0 // if ((dest | src | len) & 7 != 0) +(p6) br.cond.sptk .next // goto next; + +// The optimal case, when dest, src and len are all multiples of 8 + + and tmp3 = 0xf, len + mov pr.rot = 1 << 16 // set rotating predicates + mov ar.ec = MEMLAT + 1 ;; // set the epilog counter + cmp.ne p6, p0 = tmp3, r0 // do we have to copy an extra word? + adds loopcnt = -1, loopcnt;; // --loopcnt +(p6) ld8 value = [src], 8;; +(p6) st8 [dest] = value, 8 // copy the "odd" word + mov ar.lc = loopcnt // set the loop counter + cmp.eq p6, p0 = 8, len +(p6) br.cond.spnt .restore_and_exit;;// the one-word special case + adds adest = 8, dest // set adest one word ahead of dest + adds asrc = 8, src ;; // set asrc one word ahead of src + nop.b 0 // get the "golden" alignment for + nop.b 0 // the next loop +.l0: +(p[0]) ld8 r[0] = [src], 16 +(p[0]) ld8 q[0] = [asrc], 16 +(p[MEMLAT]) st8 [dest] = r[MEMLAT], 16 +(p[MEMLAT]) st8 [adest] = q[MEMLAT], 16 + br.ctop.dptk .l0 ;; + + mov pr = saved_pr, -1 // restore the predicate registers + mov ar.lc = saved_lc // restore the loop counter + br.ret.sptk.many b0 +.next: + cmp.ge p6, p0 = OP_T_THRES, len // is len <= OP_T_THRES + and loopcnt = 7, tmp2 // loopcnt = -dest % 8 +(p6) br.cond.spnt .cpyfew // copy byte by byte + ;; + cmp.eq p6, p0 = loopcnt, r0 +(p6) br.cond.sptk .dest_aligned + sub len = len, loopcnt // len -= -dest % 8 + adds loopcnt = -1, loopcnt // --loopcnt + ;; + mov ar.lc = loopcnt +.l1: // copy -dest % 8 bytes + ld1 value = [src], 1 // value = *src++ + ;; + st1 [dest] = value, 1 // *dest++ = value + br.cloop.dptk .l1 +.dest_aligned: + and sh1 = 7, src // sh1 = src % 8 + and tmp2 = -8, len // tmp2 = len & -OPSIZ + and asrc = -8, src // asrc = src & -OPSIZ -- align src + shr.u loopcnt = len, 3 // loopcnt = len / 8 + and len = 7, len;; // len = len % 8 + adds loopcnt = -1, loopcnt // --loopcnt + addl tmp4 = @ltoff(.table), gp + addl tmp3 = @ltoff(.loop56), gp + mov ar.ec = MEMLAT + 1 // set EC + mov pr.rot = 1 << 16;; // set rotating predicates + mov ar.lc = loopcnt // set LC + cmp.eq p6, p0 = sh1, r0 // is the src aligned? +(p6) br.cond.sptk .src_aligned + add src = src, tmp2 // src += len & -OPSIZ + shl sh1 = sh1, 3 // sh1 = 8 * (src % 8) + ld8 ploop56 = [tmp3] // ploop56 = &loop56 + ld8 ptable = [tmp4];; // ptable = &table + add tmp3 = ptable, sh1;; // tmp3 = &table + sh1 + mov ar.ec = MEMLAT + 1 + 1 // one more pass needed + ld8 tmp4 = [tmp3];; // tmp4 = loop offset + sub loopaddr = ploop56,tmp4 // loopadd = &loop56 - loop offset + ld8 r[1] = [asrc], 8;; // w0 + mov b6 = loopaddr;; + br b6 // jump to the appropriate loop + + LOOP(8) + LOOP(16) + LOOP(24) + LOOP(32) + LOOP(40) + LOOP(48) + LOOP(56) + +.src_aligned: +.l3: +(p[0]) ld8 r[0] = [src], 8 +(p[MEMLAT]) st8 [dest] = r[MEMLAT], 8 + br.ctop.dptk .l3 +.cpyfew: + cmp.eq p6, p0 = len, r0 // is len == 0 ? + adds len = -1, len // --len; +(p6) br.cond.spnt .restore_and_exit ;; + mov ar.lc = len +.l4: + ld1 value = [src], 1 + ;; + st1 [dest] = value, 1 + br.cloop.dptk .l4 ;; +.restore_and_exit: + mov pr = saved_pr, -1 // restore the predicate registers + mov ar.lc = saved_lc // restore the loop counter + br.ret.sptk.many b0 + +// In the case of a backward copy, optimise only the case when everything +// is a multiple of 8, otherwise copy byte by byte. The backward copy is +// used only when the blocks are overlapping and dest > src. + +.backward: + shr.u loopcnt = len, 3 // loopcnt = len / 8 + add src = src, len // src points one byte past the end + add dest = dest, len ;; // dest points one byte past the end + mov ar.ec = MEMLAT + 1 // set the epilog counter + mov pr.rot = 1 << 16 // set rotating predicates + adds loopcnt = -1, loopcnt // --loopcnt + cmp.ne p6, p0 = tmp4, r0 // if ((dest | src | len) & 7 != 0) +(p6) br.cond.sptk .bytecopy ;; // copy byte by byte backward + adds src = -8, src // src points to the last word + adds dest = -8, dest // dest points to the last word + mov ar.lc = loopcnt;; // set the loop counter +.l5: +(p[0]) ld8 r[0] = [src], -8 +(p[MEMLAT]) st8 [dest] = r[MEMLAT], -8 + br.ctop.dptk .l5 + br.cond.sptk .restore_and_exit +.bytecopy: + adds src = -1, src // src points to the last byte + adds dest = -1, dest // dest points to the last byte + adds loopcnt = -1, len;; // loopcnt = len - 1 + mov ar.lc = loopcnt;; // set the loop counter +.l6: +(p[0]) ld1 r[0] = [src], -1 +(p[MEMLAT]) st1 [dest] = r[MEMLAT], -1 + br.ctop.dptk .l6 + br.cond.sptk .restore_and_exit +.table: + data8 0 // dummy entry + data8 .loop56 - .loop8 + data8 .loop56 - .loop16 + data8 .loop56 - .loop24 + data8 .loop56 - .loop32 + data8 .loop56 - .loop40 + data8 .loop56 - .loop48 + data8 .loop56 - .loop56 + + .size memmove, . - memove + .endp memmove diff --git a/src/arch/ia64/core/memset.S b/src/arch/ia64/core/memset.S new file mode 100644 index 00000000..a6cc40fc --- /dev/null +++ b/src/arch/ia64/core/memset.S @@ -0,0 +1,133 @@ +/* + * Copyright (C) 1999-2002 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO 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. + * + * ELILO 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 ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + * + * This code is derived from the Linux/ia64 source code. + */ + +/* + * + * Optimized version of the standard memset() function + * + * Return: none + * + * Inputs: + * in0: address of buffer + * in1: byte value to use for storing + * in2: length of the buffer + * + */ + +// arguments +// +#define buf r32 +#define val r33 +#define len r34 + +// +// local registers +// +#define saved_pfs r14 +#define cnt r18 +#define buf2 r19 +#define saved_lc r20 +#define tmp r21 + .text + .global memset + .proc memset +memset: + .prologue + .save ar.pfs, saved_pfs + alloc saved_pfs=ar.pfs,3,0,0,0 // cnt is sink here + cmp.eq p8,p0=r0,len // check for zero length + .save ar.lc, saved_lc + mov saved_lc=ar.lc // preserve ar.lc (slow) + ;; + + .body + + adds tmp=-1,len // br.ctop is repeat/until + tbit.nz p6,p0=buf,0 // odd alignment +(p8) br.ret.spnt.few rp + + cmp.lt p7,p0=16,len // if len > 16 then long memset + mux1 val=val,@brcst // prepare value +(p7) br.cond.dptk.few long_memset + ;; + mov ar.lc=tmp // initialize lc for small count + ;; // avoid RAW and WAW on ar.lc +1: // worst case 15 cyles, avg 8 cycles + st1 [buf]=val,1 + br.cloop.dptk.few 1b + ;; // avoid RAW on ar.lc + mov ar.lc=saved_lc + mov ar.pfs=saved_pfs + br.ret.sptk.few rp // end of short memset + + // at this point we know we have more than 16 bytes to copy + // so we focus on alignment +long_memset: +(p6) st1 [buf]=val,1 // 1-byte aligned +(p6) adds len=-1,len;; // sync because buf is modified + tbit.nz p6,p0=buf,1 + ;; +(p6) st2 [buf]=val,2 // 2-byte aligned +(p6) adds len=-2,len;; + tbit.nz p6,p0=buf,2 + ;; +(p6) st4 [buf]=val,4 // 4-byte aligned +(p6) adds len=-4,len;; + tbit.nz p6,p0=buf,3 + ;; +(p6) st8 [buf]=val,8 // 8-byte aligned +(p6) adds len=-8,len;; + shr.u cnt=len,4 // number of 128-bit (2x64bit) words + ;; + cmp.eq p6,p0=r0,cnt + adds tmp=-1,cnt +(p6) br.cond.dpnt.few .dotail // we have less than 16 bytes left + ;; + adds buf2=8,buf // setup second base pointer + mov ar.lc=tmp + ;; +2: // 16bytes/iteration + st8 [buf]=val,16 + st8 [buf2]=val,16 + br.cloop.dptk.few 2b + ;; +.dotail: // tail correction based on len only + tbit.nz p6,p0=len,3 + ;; +(p6) st8 [buf]=val,8 // at least 8 bytes + tbit.nz p6,p0=len,2 + ;; +(p6) st4 [buf]=val,4 // at least 4 bytes + tbit.nz p6,p0=len,1 + ;; +(p6) st2 [buf]=val,2 // at least 2 bytes + tbit.nz p6,p0=len,0 + mov ar.lc=saved_lc + ;; +(p6) st1 [buf]=val // only 1 byte left + br.ret.dptk.few rp + .endp memset diff --git a/src/arch/ia64/core/pal.c b/src/arch/ia64/core/pal.c new file mode 100644 index 00000000..cfb518d0 --- /dev/null +++ b/src/arch/ia64/core/pal.c @@ -0,0 +1,84 @@ +#include "etherboot.h" +#include "sal.h" +#include "pal.h" + +struct fptr pal_entry; +/* + * Note that some of these calls use a static-register only calling + * convention which has nothing to do with the regular calling + * convention. + */ +#define PAL_CACHE_FLUSH 1 /* flush i/d cache */ +#define PAL_CACHE_INFO 2 /* get detailed i/d cache info */ +#define PAL_CACHE_INIT 3 /* initialize i/d cache */ +#define PAL_CACHE_SUMMARY 4 /* get summary of cache heirarchy */ +#define PAL_MEM_ATTRIB 5 /* list supported memory attributes */ +#define PAL_PTCE_INFO 6 /* purge TLB info */ +#define PAL_VM_INFO 7 /* return supported virtual memory features */ +#define PAL_VM_SUMMARY 8 /* return summary on supported vm features */ +#define PAL_BUS_GET_FEATURES 9 /* return processor bus interface features settings */ +#define PAL_BUS_SET_FEATURES 10 /* set processor bus features */ +#define PAL_DEBUG_INFO 11 /* get number of debug registers */ +#define PAL_FIXED_ADDR 12 /* get fixed component of processors's directed address */ +#define PAL_FREQ_BASE 13 /* base frequency of the platform */ +#define PAL_FREQ_RATIOS 14 /* ratio of processor, bus and ITC frequency */ +#define PAL_PERF_MON_INFO 15 /* return performance monitor info */ +#define PAL_PLATFORM_ADDR 16 /* set processor interrupt block and IO port space addr */ +#define PAL_PROC_GET_FEATURES 17 /* get configurable processor features & settings */ +#define PAL_PROC_SET_FEATURES 18 /* enable/disable configurable processor features */ +#define PAL_RSE_INFO 19 /* return rse information */ +#define PAL_VERSION 20 /* return version of PAL code */ +#define PAL_MC_CLEAR_LOG 21 /* clear all processor log info */ +#define PAL_MC_DRAIN 22 /* drain operations which could result in an MCA */ +#define PAL_MC_EXPECTED 23 /* set/reset expected MCA indicator */ +#define PAL_MC_DYNAMIC_STATE 24 /* get processor dynamic state */ +#define PAL_MC_ERROR_INFO 25 /* get processor MCA info and static state */ +#define PAL_MC_RESUME 26 /* Return to interrupted process */ +#define PAL_MC_REGISTER_MEM 27 /* Register memory for PAL to use during MCAs and inits */ +#define PAL_HALT 28 /* enter the low power HALT state */ +#define PAL_HALT_LIGHT 29 /* enter the low power light halt state*/ +#define PAL_COPY_INFO 30 /* returns info needed to relocate PAL */ +#define PAL_CACHE_LINE_INIT 31 /* init tags & data of cache line */ +#define PAL_PMI_ENTRYPOINT 32 /* register PMI memory entry points with the processor */ +#define PAL_ENTER_IA_32_ENV 33 /* enter IA-32 system environment */ +#define PAL_VM_PAGE_SIZE 34 /* return vm TC and page walker page sizes */ + +#define PAL_MEM_FOR_TEST 37 /* get amount of memory needed for late processor test */ +#define PAL_CACHE_PROT_INFO 38 /* get i/d cache protection info */ +#define PAL_REGISTER_INFO 39 /* return AR and CR register information*/ +#define PAL_SHUTDOWN 40 /* enter processor shutdown state */ +#define PAL_PREFETCH_VISIBILITY 41 + +#define PAL_COPY_PAL 256 /* relocate PAL procedures and PAL PMI */ +#define PAL_HALT_INFO 257 /* return the low power capabilities of processor */ +#define PAL_TEST_PROC 258 /* perform late processor self-test */ +#define PAL_CACHE_READ 259 /* read tag & data of cacheline for diagnostic testing */ +#define PAL_CACHE_WRITE 260 /* write tag & data of cacheline for diagnostic testing */ +#define PAL_VM_TR_READ 261 /* read contents of translation register */ + + +/* + * Get the ratios for processor frequency, bus frequency and interval timer to + * to base frequency of the platform + */ +long pal_freq_ratios(struct pal_freq_ratio *proc_ratio, + struct pal_freq_ratio *bus_ratio, struct pal_freq_ratio *itc_ratio) +{ + struct freq_ratios { + long status; + struct pal_freq_ratio proc_ratio; + struct pal_freq_ratio bus_ratio; + struct pal_freq_ratio itc_ratio; + }; + struct freq_ratios result; + extern struct freq_ratios pal_call(unsigned long which, ...); + result = pal_call(PAL_FREQ_RATIOS, 0, 0, 0); + if (proc_ratio) + *proc_ratio = result.proc_ratio; + if (bus_ratio) + *bus_ratio = result.bus_ratio; + if (itc_ratio) + *itc_ratio = result.itc_ratio; + return result.status; + +} diff --git a/src/arch/ia64/core/pci_io.c b/src/arch/ia64/core/pci_io.c new file mode 100644 index 00000000..f8069bb9 --- /dev/null +++ b/src/arch/ia64/core/pci_io.c @@ -0,0 +1,62 @@ +#include "etherboot.h" +#include "pci.h" +#include "sal.h" + +int pcibios_read_config_byte(unsigned int bus, unsigned int devfn, unsigned int reg, uint8_t *rvalue) +{ + unsigned long value; + long result; + result = sal_pci_config_read(PCI_SAL_ADDRESS(0,bus, 0, devfn, reg), 1, &value); + *rvalue = value; + return result; +} +int pcibios_read_config_word(unsigned int bus, unsigned int devfn, unsigned int reg, uint16_t *rvalue) +{ + unsigned long value; + long result; + result = sal_pci_config_read(PCI_SAL_ADDRESS(0,bus, 0, devfn, reg), 2, &value); + *rvalue = value; + return result; +} +int pcibios_read_config_dword(unsigned int bus, unsigned int devfn, unsigned int reg, uint32_t *rvalue) +{ + unsigned long value; + long result; + result = sal_pci_config_read(PCI_SAL_ADDRESS(0,bus, 0, devfn, reg), 4, &value); + *rvalue = value; + return result; +} + +int pcibios_write_config_byte(unsigned int bus, unsigned int devfn, unsigned int reg, uint8_t value) +{ + return sal_pci_config_write(PCI_SAL_ADDRESS(0,bus, 0, devfn, reg), 1, value); +} + +int pcibios_write_config_word(unsigned int bus, unsigned int devfn, unsigned int reg, uint16_t value) +{ + return sal_pci_config_write(PCI_SAL_ADDRESS(0,bus, 0, devfn, reg), 2, value); +} + +int pcibios_write_config_dword(unsigned int bus, unsigned int devfn, unsigned int reg, uint32_t value) +{ + return sal_pci_config_write(PCI_SAL_ADDRESS(0,bus, 0, devfn, reg), 4, value); +} + +/* So far I have not see a non-zero PCI_BUS_OFFSET + * and an AML parser to get it much to much trouble. + */ +#ifndef PCI_BUS_OFFSET +#define PCI_BUS_OFFSET 0 +#endif + +unsigned long pcibios_bus_base(unsigned int bus) +{ + return PCI_BUS_OFFSET; +} + +void find_pci(int type, struct pci_device *dev) +{ + /* Should I check for sal functions being present? */ + return scan_pci_bus(type, dev); +} + diff --git a/src/arch/ia64/core/reloc.S b/src/arch/ia64/core/reloc.S new file mode 100644 index 00000000..31689bfb --- /dev/null +++ b/src/arch/ia64/core/reloc.S @@ -0,0 +1,133 @@ +/* reloc.S - position independent IA-64 ELF shared object relocator + Copyright (C) 1999 Hewlett-Packard Co. + Contributed by David Mosberger . + Copyright (C) 2002 Eric Biederman sponsored by Linux Networx + + This file is part of etherboot. + This file was derived from reloc_ia64.S from GNU-EFI, the GNU EFI development environment. + + GNU EFI 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. + + GNU EFI 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 GNU EFI; see the file COPYING. If not, write to the Free + Software Foundation, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +/* + * This is written in assembly because the entire code needs to be position + * independent. Note that the compiler does not generate code that's position + * independent by itself because it relies on the global offset table being + * relocated. + * + * This code assumes the code was compiled with -mconstant-gp -mauto-pic -static -shared + * Which generates position independent code, but not position indepedent data. + * This code assumes in the linker script the rela entries are bracked with: + * _rela, _erela and _rela_size gives the total size of the rela entries. + * This gives a much smaller binary than when compiled as a true shared object. + * + * This code assumes the original shared object was initially relocated, + * So that it only needs to apply changes for the new address the code is linked + * at. + */ + .text + .psr abi64 + .psr lsb + .lsb + + +#define ST_VALUE_OFF 8 /* offset of st_value in elf sym */ + +#define RET_SUCCESS 0 +#define RET_LOAD_ERROR 1 + +#define R_IA64_NONE 0 +#define R_IA64_REL64MSB 0x6e +#define R_IA64_REL64LSB 0x6f +#define R_IA64_DIR64MSB 0x26 +#define R_IA64_DIR64LSB 0x27 +#define R_IA64_FPTR64MSB 0x46 +#define R_IA64_FPTR64LSB 0x47 + +#define delta in0 /* Chaing in load address (address of .text) */ + +#define ldbase r15 +#define target r16 +#define val r17 +#define rela r18 +#define relasz r19 +#define relaent r20 +#define addr r21 +#define r_info r22 +#define r_offset r23 +#define r_type r25 + +#define Pmore p6 + +#define Pnone p6 +#define Prel p7 +#define Pdir p8 + + + .global _relocate +_relocate: + alloc r2=ar.pfs,1,0,0,0 + add rela=@gprel(_rela),gp + add ldbase=@gprel(_text),gp + add r3=@ltoff(_rela_size),gp + ;; + ld8 relasz = [r3] + mov relaent=24 + br.sptk.few apply_relocs + +apply_loop: + ld8 r_offset = [rela] + add addr = 8,rela + sub relasz = relasz,relaent + + ;; + ld8 r_info = [addr], 8 + ;; + add target = ldbase, r_offset + add rela = rela,relaent + extr.u r_type = r_info, 0, 32 + ;; + cmp.eq Pnone,p0 = R_IA64_NONE, r_type + cmp.eq Prel,p0 = R_IA64_REL64LSB, r_type + cmp.eq Pdir,p0 = R_IA64_DIR64LSB, r_type /* Needed? */ + ;; +(Pnone) br.cond.sptk.few apply_relocs +(Prel) br.cond.sptk.few apply_REL64 +(Pdir) br.cond.sptk.few apply_DIR64 /* Needed? */ + ;; + +apply_error: + mov r8 = RET_LOAD_ERROR + br.ret.sptk.few rp + +apply_REL64: +apply_DIR64: + ld8 val = [target] + ;; + add val = val, delta + ;; + st8 [target] = val + ;; + /* fall through to apply_relocs */ +apply_relocs: + cmp.ltu Pmore,p0=0,relasz +(Pmore) br.cond.sptk.few apply_loop + ;; + + mov r8 = RET_SUCCESS + br.ret.sptk.few rp + + .size _relocate, . - _relocate + .endp _relocate diff --git a/src/arch/ia64/core/relocate_to.S b/src/arch/ia64/core/relocate_to.S new file mode 100644 index 00000000..37b5c9f8 --- /dev/null +++ b/src/arch/ia64/core/relocate_to.S @@ -0,0 +1,92 @@ + /* Temporarily ignore the stack, as I am still using efi's stack */ + +#define newbase in0 +#define base loc2 +#define newgp loc3 +#define len loc4 + + .explicit + .globl relocate_to + .proc relocate_to +relocate_to: + /* In incoming variable the new base addres of etherboot. */ + alloc loc0=ar.pfs,1,5,3,0 /* in, local, out, rotating */ + mov loc1=rp + + /* Compute the current location of _text */ + /* Compute the new gp value */ + add base=@gprel(_text),gp + add len =@gprel(_end),gp + movl newgp=@gprel(_text) + ;; + sub newgp=newbase,newgp /* gp = _text - @gprel(_text) */ + sub len=len,base + ;; + + /* Copy etherboot to the new location */ + mov out0=newbase + mov out1=base + mov out2=len + br.call.sptk.few rp=memcpy + ;; + + /* Jump to my __relocate_to in the new location */ + movl r14=@gprel(__relocate_to) + ;; + add r14=r14,newgp + ;; + mov b6=r14 + ;; + br.cond.sptk.few b6 + ;; +__relocate_to: + /* I am at the new location set the newgp as the default */ + mov gp=newgp + ;; + /* New apply relocations to the new copy */ + sub out0=newbase,base + br.call.sptk.few rp=_relocate + ;; + + /* Lookup restart_etherboot */ + add out0=@gprel(restart_etherboot),gp + ;; + + /* Adjust the gp and return address. + * NOTE: This only works when the setjmp can modify it's caller's gp + * address. Essentially this means etherboot must be compiled with + * compiled with -mconstant-gp, though an inline version of setjmp might work. + */ + add r14=0x40,out0 + add r16=0x08,out0 + ;; + ld8 r15=[r14] + ld8 r17=[r16] + ;; + sub r15=r15,base + sub r17=r17,base + ;; + add r15=r15,newbase + add r17=r17,newbase + ;; + st8 [r14]=r15 + st8 [r16]=r17 + ;; + mov out1=256 + br.call.sptk.few rp=longjmp + + /* And just in case lonjmp returns... */ + + + /* Adjust my return address and return */ + sub loc1=loc1,base + ;; + add loc1=loc1,newbase + ;; + mov ar.pfs=loc0 + mov rp=loc1 + ;; + br.ret.sptk.few rp + + .size relocate_to, . - relocate_to + .endp relocate_to diff --git a/src/arch/ia64/core/sal.c b/src/arch/ia64/core/sal.c new file mode 100644 index 00000000..aa040f80 --- /dev/null +++ b/src/arch/ia64/core/sal.c @@ -0,0 +1,278 @@ +#include "etherboot.h" +#include "sal.h" + +struct sal_entry_base { + uint8_t entry_type; +#define SAL_TYPE_ENTRYPOINT 0 +#define SAL_TYPE_MEMORY 1 +#define SAL_TYPE_PLATFORM_FEATURES 2 +#define SAL_TYPE_TRANSLATION_REGISTER 3 +#define SAL_TYPE_PURGE_DOMAIN 4 +#define SAL_TYPE_AP_WAKEUP 5 +}; + +struct sal_entry_point_descriptor { + uint8_t entry_type; + uint8_t reserved[7]; + uint64_t pal_proc; + uint64_t sal_proc; + uint64_t sal_gp; + uint8_t reserved2[16]; +}; + +struct sal_memory_descriptor { + uint8_t entry_type; + uint8_t sal_needs_virt_mapping; + uint8_t mem_attr; +#define MEM_ATTR_WB 0 +#define MEM_ATTR_UC 8 +#define MEM_ATTR_UCE 9 +#define MEM_ATTR_WC 10 + uint8_t access_rights; + uint8_t mem_attr_support; +#define MEM_ATTR_SUPPORTS_WB 1 +#define MEM_ATTR_SUPPORTS_UC 2 +#define MEM_ATTR_SUPPORTS_UCE 4 +#define MEM_ATTR_SUPPORTS_WC 8 + uint8_t reserved; + uint8_t mem_type; +#define MEM_TYPE_RAM 0 +#define MEM_TYPE_MIO 1 +#define MEM_TYPE_SAPIC 2 +#define MEM_TYPE_PIO 3 +#define MEM_TYPE_FIRMWARE 4 +#define MEM_TYPE_BAD_RAM 9 +#define MEM_TYPE_BLACK_HOLE 10 + uint8_t mem_usage; +#define MEM_USAGE_UNSPECIFIED 0 +#define MEM_USAGE_PAL_CODE 1 +#define MEM_USAGE_BOOT_SERVICES_CODE 2 +#define MEM_USAGE_BOOT_SERVICES_DATA 3 +#define MEM_USAGE_RUNTIME_SERVICES_CODE 4 +#define MEM_USAGE_RUNTIME_SERVICES_DATA 5 +#define MEM_USAGE_IA32_OPTION_ROM 6 +#define MEM_USAGE_IA32_SYSTEM_ROM 7 +#define MEM_USAGE_ACPI_RECLAIM_MEMORY 8 +#define MEM_USAGE_ACPI_NVS_MEMORY 9 +#define MEM_USAGE_SAL_PMI_CODE 10 +#define MEM_USAGE_SAL_PMI_DATA 11 +#define MEM_USAGE_FIRMWARE_RESERVED_RAM 12 + +#define MEM_USAGE_CPU_TO_IO 0 + uint64_t phys_address; + uint32_t pages; /* In 4k pages */ + uint32_t reserved2; + uint8_t oem_reserved[8]; +}; + +struct sal_platform_features { + uint8_t entry_type; + uint8_t feature_list; +#define SAL_FEATURE_BUS_LOCK 1 +#define SAL_FEATURE_PLATFORM_REDIRECTION_HINT 2 +#define SAL_FEATURE_PROCESSOR_REDIRECTION_HINT 3 + uint8_t reserved[14]; +}; +struct sal_translation_register { + uint8_t entry_type; + uint8_t tr_type; +#define SAL_ITR 0 +#define SAL_DTR 1 + uint8_t tr_number; + uint8_t reserved[5]; + uint64_t virtual_address; + uint64_t page_size; + uint8_t reserved2[8]; +}; + +struct sal_purge_translation_cache_coherency_domain { + uint8_t entry_type; + uint8_t reserved[3]; + uint32_t coherence_domain_count; + uint64_t coherence_domain_addr; +}; + +struct sal_ap_wakeup_descriptor { + uint8_t entry_type; + uint8_t wakeup_mechanism; + uint8_t reserved[6]; + uint64_t interrupt; +}; + +struct sal_entry { + union { + struct sal_entry_base base; + struct sal_entry_point_descriptor entry_point; + struct sal_memory_descriptor mem; + struct sal_platform_features features; + struct sal_translation_register tr; + struct sal_purge_translation_cache_coherency_domain purge; + struct sal_ap_wakeup_descriptor ap_wakeup; + }; +}; + +struct sal_system_table { + uint8_t signature[4]; /* SST_ */ + uint32_t table_length; + + uint16_t sal_rev; + uint16_t entry_count; + uint8_t checksum; + uint8_t reserved1[7]; + uint16_t sal_a_version; + uint16_t sal_b_version; + + uint8_t oem_id[32]; + uint8_t product_id[32]; + uint8_t reserved2[8]; + struct sal_entry entry[0]; +}; + +static struct sal_system_table *sal; +struct fptr sal_entry; + +int parse_sal_system_table(void *table) +{ + struct sal_system_table *salp = table; + uint8_t *ptr; + uint8_t checksum; + struct sal_entry *entry; + unsigned i; + if (memcmp(salp->signature, "SST_", 4) != 0) { + return 0; + } + ptr = table; + checksum = 0; + for(i = 0; i < salp->table_length; i++) { + checksum += ptr[i]; + } + if (checksum != 0) { + return 0; + } +#if 0 + printf("SALA: %hx SALB: %hx\n", + salp->sal_a_version, + salp->sal_b_version); + printf("SAL OEM: "); + for(i = 0; i < sizeof(salp->oem_id); i++) { + uint8_t ch = salp->oem_id[i]; + if (ch == 0) + break; + printf("%c", ch); + } + printf("\n"); + + printf("SAL PRODUCT: "); + for(i = 0; i < sizeof(salp->product_id); i++) { + uint8_t ch = salp->product_id[i]; + if (ch == 0) + break; + printf("%c", ch); + } + printf("\n"); +#endif + sal = salp; + pal_entry.entry = 0; + pal_entry.gp = 0; + sal_entry.entry = 0; + sal_entry.gp = 0; + entry = sal->entry; + i = 0; + while(i < salp->entry_count) { + unsigned long size = 0; + + switch(entry->base.entry_type) { + case SAL_TYPE_ENTRYPOINT: + size = sizeof(entry->entry_point); + pal_entry.entry = entry->entry_point.pal_proc; + sal_entry.entry = entry->entry_point.sal_proc; + sal_entry.gp = entry->entry_point.sal_gp; + break; + case SAL_TYPE_MEMORY: + size = sizeof(entry->mem); + break; + case SAL_TYPE_PLATFORM_FEATURES: + size = sizeof(entry->features); + break; + case SAL_TYPE_TRANSLATION_REGISTER: + size = sizeof(entry->tr); + break; + case SAL_TYPE_PURGE_DOMAIN: + size = sizeof(entry->purge); + break; + case SAL_TYPE_AP_WAKEUP: + size = sizeof(entry->ap_wakeup); + break; + default: + break; + } + entry = (struct sal_entry *)(((char *)entry) + size); + i++; + } + return 1; +} + +#define SAL_SET_VECTORS 0x01000000 +#define SAL_GET_STATE_INFO 0x01000001 +#define SAL_GET_STATE_INFO_SIZE 0x01000002 +#define SAL_CLEAR_STATE_INFO 0x01000003 +#define SAL_MC_RENDEZ 0x01000004 +#define SAL_MC_SET_PARAMS 0x01000005 +#define SAL_REGISTER_PHYSICAL_ADDR 0x01000006 + +#define SAL_CACHE_FLUSH 0x01000008 +#define SAL_CACHE_INIT 0x01000009 +#define SAL_PCI_CONFIG_READ 0x01000010 +#define SAL_PCI_CONFIG_WRITE 0x01000011 +#define SAL_FREQ_BASE 0x01000012 + +#define SAL_UPDATE_PAL 0x01000020 + +/* + * Now define a couple of inline functions for improved type checking + * and convenience. + */ +long sal_freq_base (unsigned long which, unsigned long *ticks_per_second, + unsigned long *drift_info) +{ + struct { + long status; + unsigned long ticks_per_second; + unsigned long drift_info; + } result, __call(void *,...); + + result = __call(&sal_entry, SAL_FREQ_BASE, which, 0, 0, 0, 0, 0, 0); + + *ticks_per_second = result.ticks_per_second; + *drift_info = result.drift_info; + return result.status; +} + + + +/* Read from PCI configuration space */ +long sal_pci_config_read ( + unsigned long pci_config_addr, unsigned long size, unsigned long *value) +{ + struct { + long status; + unsigned long value; + } result, __call(void *,...); + + result = __call(&sal_entry, SAL_PCI_CONFIG_READ, pci_config_addr, size, 0, 0, 0, 0, 0); + if (value) + *value = result.value; + return result.status; +} + +/* Write to PCI configuration space */ +long sal_pci_config_write ( + unsigned long pci_config_addr, unsigned long size, unsigned long value) +{ + struct { + long status; + } result, __call(void *,...); + + result = __call(&sal_entry, SAL_PCI_CONFIG_WRITE, pci_config_addr, size, value, 0, 0, 0, 0); + return result.status; +} diff --git a/src/arch/ia64/core/setjmp.S b/src/arch/ia64/core/setjmp.S new file mode 100644 index 00000000..f3e57678 --- /dev/null +++ b/src/arch/ia64/core/setjmp.S @@ -0,0 +1,173 @@ +/* Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. + Contributed by David Mosberger-Tang . + + The GNU C 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. + + The GNU C 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 the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. + + The layout of the jmp_buf is as follows. This is subject to change + and user-code should never depend on the particular layout of + jmp_buf! + + + offset: description: + ------- ------------ + 0x000 stack pointer (r12) ; unchangeable (see _JMPBUF_UNWINDS) + 0x008 r1 (gp) + 0x010 caller's unat + 0x018 fpsr + 0x020 r4 + 0x028 r5 + 0x030 r6 + 0x038 r7 + 0x040 rp (b0) + 0x048 b1 + 0x050 b2 + 0x058 b3 + 0x060 b4 + 0x068 b5 + 0x070 ar.pfs + 0x078 ar.lc + 0x080 pr + 0x088 ar.bsp ; unchangeable (see __longjmp.S) + 0x090 ar.unat + 0x098 &__jmp_buf ; address of the jmpbuf (needed to locate NaT bits in unat) + 0x0a0 f2 + 0x0b0 f3 + 0x0c0 f4 + 0x0d0 f5 + 0x0e0 f16 + 0x0f0 f17 + 0x100 f18 + 0x110 f19 + 0x120 f20 + 0x130 f21 + 0x130 f22 + 0x140 f23 + 0x150 f24 + 0x160 f25 + 0x170 f26 + 0x180 f27 + 0x190 f28 + 0x1a0 f29 + 0x1b0 f30 + 0x1c0 f31 */ + + + /* The following two entry points are the traditional entry points: */ + + .text + .global setjmp + .proc setjmp +setjmp: + alloc r8=ar.pfs,2,0,0,0 + mov in1=1 + br.cond.sptk.many __sigsetjmp + .size setjmp, . - setjmp + .endp setjmp + + /* __sigsetjmp(__jmp_buf buf, int savemask) */ + +__sigsetjmp: +#if 0 + .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(2) +#endif + alloc loc1=ar.pfs,2,2,2,0 + mov r16=ar.unat + ;; + mov r17=ar.fpsr + mov r2=in0 + add r3=8,in0 + ;; + st8.spill.nta [r2]=sp,16 // r12 (sp) + st8.spill.nta [r3]=gp,16 // r1 (gp) + ;; + st8.nta [r2]=r16,16 // save caller's unat + st8.nta [r3]=r17,16 // save fpsr + add r8=0xa0,in0 + ;; + st8.spill.nta [r2]=r4,16 // r4 + st8.spill.nta [r3]=r5,16 // r5 + add r9=0xb0,in0 + ;; + stf.spill.nta [r8]=f2,32 + stf.spill.nta [r9]=f3,32 + mov loc0=rp + .body + ;; + stf.spill.nta [r8]=f4,32 + stf.spill.nta [r9]=f5,32 + mov r17=b1 + ;; + stf.spill.nta [r8]=f16,32 + stf.spill.nta [r9]=f17,32 + mov r18=b2 + ;; + stf.spill.nta [r8]=f18,32 + stf.spill.nta [r9]=f19,32 + mov r19=b3 + ;; + stf.spill.nta [r8]=f20,32 + stf.spill.nta [r9]=f21,32 + mov r20=b4 + ;; + stf.spill.nta [r8]=f22,32 + stf.spill.nta [r9]=f23,32 + mov r21=b5 + ;; + stf.spill.nta [r8]=f24,32 + stf.spill.nta [r9]=f25,32 + mov r22=ar.lc + ;; + stf.spill.nta [r8]=f26,32 + stf.spill.nta [r9]=f27,32 + mov r24=pr + ;; + stf.spill.nta [r8]=f28,32 + stf.spill.nta [r9]=f29,32 + ;; + stf.spill.nta [r8]=f30 + stf.spill.nta [r9]=f31 + + st8.spill.nta [r2]=r6,16 // r6 + st8.spill.nta [r3]=r7,16 // r7 + ;; + mov r23=ar.bsp + mov r25=ar.unat + mov out0=in0 + + st8.nta [r2]=loc0,16 // b0 + st8.nta [r3]=r17,16 // b1 + mov out1=in1 + ;; + st8.nta [r2]=r18,16 // b2 + st8.nta [r3]=r19,16 // b3 + ;; + st8.nta [r2]=r20,16 // b4 + st8.nta [r3]=r21,16 // b5 + ;; + st8.nta [r2]=loc1,16 // ar.pfs + st8.nta [r3]=r22,16 // ar.lc + ;; + st8.nta [r2]=r24,16 // pr + st8.nta [r3]=r23,16 // ar.bsp + ;; + st8.nta [r2]=r25 // ar.unat + st8.nta [r3]=in0 // &__jmp_buf + mov r8=0 + mov rp=loc0 + mov ar.pfs=loc1 + br.ret.sptk.many rp + + .endp __sigsetjmp diff --git a/src/arch/ia64/core/start.S b/src/arch/ia64/core/start.S new file mode 100644 index 00000000..72b94ce1 --- /dev/null +++ b/src/arch/ia64/core/start.S @@ -0,0 +1,28 @@ + .text + .align 4 + .proc _start + .globl _start +_start: + { + alloc loc0 = ar.pfs,1,2,1,0 /* in, local, out, rotating */ + mov loc1 = rp + mov r14 = ip /* Get the address of _start */ + } + movl r15 = @gprel(_start) + ;; + sub gp = r14,r15 + ;; + rsm psr.i /* disable interrupts */ + ;; + add out0 = @gprel(_text),gp + br.call.sptk.few rp = _relocate + ;; + cmp.eq p6,p7 = r0,r8 /* r8 == SUCCESS? */ + mov ar.pfs = loc0 + mov rp = loc1 + ;; +(p6) br.cond.sptk.few main +(p7) br.ret.sptk.few rp + + .size _start, . - _start + .endp _start diff --git a/src/arch/ia64/drivers/net/undi_nii.c b/src/arch/ia64/drivers/net/undi_nii.c new file mode 100644 index 00000000..c1aabf00 --- /dev/null +++ b/src/arch/ia64/drivers/net/undi_nii.c @@ -0,0 +1,1079 @@ +#include "efi/efi.h" +#include "etherboot.h" +#include "isa.h" +#include "dev.h" +#include "nic.h" +#include "timer.h" + +#warning "Place the declaraction of __call someplace more appropriate\n" +extern EFI_STATUS __call(void *,...); +#warning "Place a declaration of lookup_efi_nic somewhere useful" +EFI_NETWORK_INTERFACE_IDENTIFIER_INTERFACE *lookup_efi_nic(int index); + +struct sw_undi { + uint8_t signature[4]; + uint8_t len; + uint8_t fudge; + uint8_t rev; + uint8_t ifcnt; + uint8_t major; + uint8_t minor; + uint16_t reserved1; + uint32_t implementation; +#define UNDI_IMP_CMD_COMPLETE_INT_SUPPORTED 0x00000001 +#define UNDI_IMP_PACKET_RX_INT_SUPPORTED 0x00000002 +#define UNDI_IMP_TX_COMPLETE_INT_SUPPORTED 0x00000004 +#define UNDI_IMP_SOFTWARE_INT_SUPPORTED 0x00000008 +#define UNDI_IMP_FILTERED_MULTICAST_RX_SUPPORTED 0x00000010 +#define UNDI_IMP_BROADCAST_RX_SUPPORTED 0x00000020 +#define UNDI_IMP_PROMISCUOUS_RX_SUPPORTED 0x00000040 +#define UNDI_IMP_PROMISCUOUS_MULTICAST_RX_SUPPORTED 0x00000080 +#define UNDI_IMP_STATION_ADDR_SETTABLE 0x00000100 +#define UNDI_IMP_STATISTICS_SUPPORTED 0x00000200 +#define UNDI_IMP_NVDATA_SUPPORT_MASK 0x00000C00 +#define UNDI_IMP_NVDATA_NOT_AVAILABLE 0x00000000 +#define UNDI_IMP_NVDATA_READ_ONLY 0x00000400 +#define UNDI_IMP_NVDATA_SPARSE_WRITEABLE 0x00000800 +#define UNDI_IMP_NVDATA_BULK_WRITEABLE 0x00000C00 +#define UNDI_IMP_MULTI_FRAME_SUPPORTED 0x00001000 +#define UNDI_IMP_CMD_QUEUE_SUPPORTED 0x00002000 +#define UNDI_IMP_CMD_LINK_SUPPORTED 0x00004000 +#define UNDI_IMP_FRAG_SUPPORTED 0x00008000 +#define UNDI_IMP_64BIT_DEVICE 0x00010000 +#define UNDI_IMP_SW_VIRT_ADDR 0x40000000 +#define UNDI_IMP_HW_UNDI 0x80000000 + uint64_t entry_point; + uint8_t reserved2[3]; + uint8_t bus_type_cnt; + uint32_t bus_type[0]; +}; + +struct cdb { + uint16_t op_code; +#define CDB_OP_GET_STATE 0x0000 +#define CDB_OP_START 0x0001 +#define CDB_OP_STOP 0x0002 +#define CDB_OP_GET_INIT_INFO 0x0003 +#define CDB_OP_GET_CONFIG_INFO 0x0004 +#define CDB_OP_INITIALIZE 0x0005 +#define CDB_OP_RESET 0x0006 +#define CDB_OP_SHUTDOWN 0x0007 +#define CDB_OP_INTERRUPT_ENABLES 0x0008 +#define CDB_OP_RECEIVE_FILTERS 0x0009 +#define CDB_OP_STATION_ADDRESS 0x000a +#define CDB_OP_STATISTICS 0x000b +#define CDB_OP_MCAST_IP_TO_MAC 0x000c +#define CDB_OP_NVDATA 0x000d +#define CDB_OP_GET_STATUS 0x000e +#define CDB_OP_FILL_HEADER 0x000f +#define CDB_OP_TRANSMIT 0x0010 +#define CDB_OP_RECEIVE 0x0011 + uint16_t op_flags; +#define CDB_OPFLAGS_NOT_USED 0x0000 +/* Initialize */ +#define CDB_OPFLAGS_INIT_CABLE_DETECT_MASK 0x0001 +#define CDB_OPFLAGS_INIT_DETECT_CABLE 0x0000 +#define CDB_OPFLAGS_INIT_DO_NOT_DETECT_CABLE 0x0001 +/* Reset */ +#define CDB_OPFLAGS_RESET_DISABLE_INTERRUPTS 0x0001 +#define CDB_OPFLAGS_RESET_DISABLE_FILTERS 0x0002 +/* Interrupt Enables */ +#define CDB_OPFLAGS_INTERRUPT_OPMASK 0xc000 +#define CDB_OPFLAGS_INTERRUPT_ENABLE 0x8000 +#define CDB_OPFLAGS_INTERRUPT_DISABLE 0x4000 +#define CDB_OPFLAGS_INTERRUPT_READ 0x0000 +#define CDB_OPFLAGS_INTERRUPT_RECEIVE 0x0001 +#define CDB_OPFLAGS_INTERRUPT_TRANSMIT 0x0002 +#define CDB_OPFLAGS_INTERRUPT_COMMAND 0x0004 +#define CDB_OPFLAGS_INTERRUPT_SOFTWARE 0x0008 +/* Receive Filters */ +#define CDB_OPFLAGS_RECEIVE_FILTER_OPMASK 0xc000 +#define CDB_OPFLAGS_RECEIVE_FILTER_ENABLE 0x8000 +#define CDB_OPFLAGS_RECEIVE_FILTER_DISABLE 0x4000 +#define CDB_OPFLAGS_RECEIVE_FILTER_READ 0x0000 +#define CDB_OPFLAGS_RECEIVE_FILTER_RESET_MCAST_LIST 0x2000 +#define CDB_OPFLAGS_RECEIVE_FILTER_UNICAST 0x0001 +#define CDB_OPFLAGS_RECEIVE_FILTER_BROADCAST 0x0002 +#define CDB_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST 0x0004 +#define CDB_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS 0x0008 +#define CDB_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST 0x0010 +/* Station Address */ +#define CDB_OPFLAGS_STATION_ADDRESS_READ 0x0000 +#define CDB_OPFLAGS_STATION_ADDRESS_WRITE 0x0000 +#define CDB_OPFLAGS_STATION_ADDRESS_RESET 0x0001 +/* Statistics */ +#define CDB_OPFLAGS_STATISTICS_READ 0x0000 +#define CDB_OPFLAGS_STATISTICS_RESET 0x0001 +/* MCast IP to MAC */ +#define CDB_OPFLAGS_MCAST_IP_TO_MAC_OPMASK 0x0003 +#define CDB_OPFLAGS_MCAST_IPV4_TO_MAC 0x0000 +#define CDB_OPFLAGS_MCAST_IPV6_TO_MAC 0x0001 +/* NvData */ +#define CDB_OPFLAGS_NVDATA_OPMASK 0x0001 +#define CDB_OPFLAGS_NVDATA_READ 0x0000 +#define CDB_OPFLAGS_NVDATA_WRITE 0x0001 +/* Get Status */ +#define CDB_OPFLAGS_GET_INTERRUPT_STATUS 0x0001 +#define CDB_OPFLAGS_GET_TRANSMITTED_BUFFERS 0x0002 +/* Fill Header */ +#define CDB_OPFLAGS_FILL_HEADER_OPMASK 0x0001 +#define CDB_OPFLAGS_FILL_HEADER_FRAGMENTED 0x0001 +#define CDB_OPFLAGS_FILL_HEADER_WHOLE 0x0000 +/* Transmit */ +#define CDB_OPFLAGS_SWUNDI_TRANSMIT_OPMASK 0x0001 +#define CDB_OPFLAGS_TRANSMIT_BLOCK 0x0001 +#define CDB_OPFLAGS_TRANSMIT_DONT_BLOCK 0x0000 + +#define CDB_OPFLAGS_TRANSMIT_OPMASK 0x0002 +#define CDB_OPFLAGS_TRANSMIT_FRAGMENTED 0x0002 +#define CDB_OPFLAGS_TRANSMIT_WHOLE 0x0000 +/* Receive */ + uint16_t cpb_size; + uint16_t db_size; + uint64_t cpb_addr; + uint64_t db_addr; + uint16_t stat_code; +#define CDB_STATCODE_INITIALIZE 0x0000 +/* Common stat_code values */ +#define CDB_STATCODE_SUCCESS 0x0000 +#define CDB_STATCODE_INVALID_CDB 0x0001 +#define CDB_STATCODE_INVALID_CPB 0x0002 +#define CDB_STATCODE_BUSY 0x0003 +#define CDB_STATCODE_QUEUE_FULL 0x0004 +#define CDB_STATCODE_ALREADY_STARTED 0x0005 +#define CDB_STATCODE_NOT_STARTED 0x0006 +#define CDB_STATCODE_NOT_SHUTDOWN 0x0007 +#define CDB_STATCODE_ALREADY_INITIALIZED 0x0008 +#define CDB_STATCODE_NOT_INITIALIZED 0x0009 +#define CDB_STATCODE_DEVICE_FAILURE 0x000A +#define CDB_STATCODE_NVDATA_FAILURE 0x000B +#define CDB_STATCODE_UNSUPPORTED 0x000C +#define CDB_STATCODE_BUFFER_FULL 0x000D +#define CDB_STATCODE_INVALID_PARAMETER 0x000E +#define CDB_STATCODE_INVALID_UNDI 0x000F +#define CDB_STATCODE_IPV4_NOT_SUPPORTED 0x0010 +#define CDB_STATCODE_IPV6_NOT_SUPPORTED 0x0011 +#define CDB_STATCODE_NOT_ENOUGH_MEMORY 0x0012 +#define CDB_STATCODE_NO_DATA 0x0013 + + uint16_t stat_flags; +#define CDB_STATFLAGS_INITIALIZE 0x0000 +/* Common stat_flags */ +#define CDB_STATFLAGS_STATUS_MASK 0xc000 +#define CDB_STATFLAGS_COMMAND_COMPLETE 0xc000 +#define CDB_STATFLAGS_COMMAND_FAILED 0x8000 +#define CDB_STATFLAGS_COMMAND_QUEUED 0x4000 +/* Get State */ +#define CDB_STATFLAGS_GET_STATE_MASK 0x0003 +#define CDB_STATFLAGS_GET_STATE_INITIALIZED 0x0002 +#define CDB_STATFLAGS_GET_STATE_STARTED 0x0001 +#define CDB_STATFLAGS_GET_STATE_STOPPED 0x0000 +/* Start */ +/* Get Init Info */ +#define CDB_STATFLAGS_CABLE_DETECT_MASK 0x0001 +#define CDB_STATFLAGS_CABLE_DETECT_NOT_SUPPORTED 0x0000 +#define CDB_STATFLAGS_CABLE_DETECT_SUPPORTED 0x0001 +/* Initialize */ +#define CDB_STATFLAGS_INITIALIZED_NO_MEDIA 0x0001 +/* Reset */ +#define CDB_STATFLAGS_RESET_NO_MEDIA 0x0001 +/* Shutdown */ +/* Interrupt Enables */ +#define CDB_STATFLAGS_INTERRUPT_RECEIVE 0x0001 +#define CDB_STATFLAGS_INTERRUPT_TRANSMIT 0x0002 +#define CDB_STATFLAGS_INTERRUPT_COMMAND 0x0004 +/* Receive Filters */ +#define CDB_STATFLAGS_RECEIVE_FILTER_UNICAST 0x0001 +#define CDB_STATFLAGS_RECEIVE_FILTER_BROADCAST 0x0002 +#define CDB_STATFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST 0x0004 +#define CDB_STATFLAGS_RECEIVE_FILTER_PROMISCUOUS 0x0008 +#define CDB_STATFLAGS_RECEIVE_FILTER_ALL_MULTICAST 0x0010 +/* Statistics */ +/* MCast IP to MAC */ +/* NvData */ +/* Get Status */ +#define CDB_STATFLAGS_GET_STATUS_INTERRUPT_MASK 0x000F +#define CDB_STATFLAGS_GET_STATUS_NO_INTERRUPTS 0x0000 +#define CDB_STATFLAGS_GET_STATUS_RECEIVE 0x0001 +#define CDB_STATFLAGS_GET_STATUS_TRANSMIT 0x0002 +#define CDB_STATFLAGS_GET_STATUS_COMMAND 0x0004 +#define CDB_STATFLAGS_GET_STATUS_SOFTWARE 0x0008 +#define CDB_STATFLAGS_GET_STATUS_TXBUF_QUEUE_EMPTY 0x0010 +#define CDB_STATFLAGS_GET_STATUS_NO_TXBUFS_WRITTEN 0x0020 +/* Fill Header */ +/* Transmit */ +/* Receive */ + uint16_t ifnum; +#define CDB_IFNUM_START 0x0000 +#define CDB_IFNUM_INVALID 0x0000 + uint16_t control; +#define CDB_CONTROL_QUEUE_IF_BUSY 0x0002 + +#define CDB_CONTROL_LINK 0x0001 +#define CDB_CONTROL_LAST_CDB_IN_LIST 0x0000 +}; + +#define UNDI_MAC_LENGTH 32 +typedef uint8_t undi_mac_addr[UNDI_MAC_LENGTH]; +typedef uint16_t undi_media_protocol; +typedef uint8_t undi_frame_type; +#define UNDI_FRAME_TYPE_NONE 0x00 +#define UNDI_FRAME_TYPE_UNICAST 0x01 +#define UNDI_FRAME_TYPE_BROADCAST 0x02 +#define UNDI_FRAME_TYPE_MULTICAST 0x03 +#define UNDI_FRAME_TYPE_PROMISCUOUS 0x04 + +#define UNDI_MAX_XMIT_BUFFERS 32 +#define UNDI_MAX_MCAST_ADDRESS_CNT 8 + +#define UNDI_BUS_TYPE(a,b,c,d) \ + ((((d) & 0xff) << 24) | \ + (((c) & 0xff) << 16) | \ + (((b) & 0xff) << 8) | \ + (((a) & 0xff) << 0)) + +#define UNDI_BUS_TYPE_PCI UNDI_BUS_TYPE('P','C','I','R') +#define UNDI_BUS_TYPE_PCC UNDI_BUS_TYPE('P','C','C','R') +#define UNDI_BUS_TYPE_USB UNDI_BUS_TYPE('U','S','B','R') +#define UNDI_BUS_TYPE_1394 UNDI_BUS_TYPE('1','3','9','4') + +struct cpb_start { + void *delay; + void *block; + void *virt2phys; + void *mem_io; +} PACKED; + +struct db_init_info { + uint32_t memory_required; + uint32_t frame_data_len; + uint32_t link_speeds[4]; + uint32_t nv_count; + uint16_t nv_width; + uint16_t media_header_len; + uint16_t hw_addr_len; + uint16_t mcast_filter_cnt; + uint16_t tx_buf_cnt; + uint16_t tx_buf_size; + uint16_t rx_buf_cnt; + uint16_t rx_buf_size; + uint8_t if_type; + uint8_t duplex; +#define UNDI_DUPLEX_ENABLE_FULL_SUPPORTED 1 +#define UNDI_DUPLEX_FORCE_FULL_SUPPORTED 2 + uint8_t loopback; +#define UNDI_LOOPBACK_INTERNAL_SUPPORTED 1 +#define UNDI_LOOPBACK_EXTERNAL_SUPPORTED 2 +} PACKED; + + +struct db_pci_config_info { + uint32_t bus_type; + uint16_t bus; + uint8_t device; + uint8_t function; + uint8_t config[256]; +}; +struct db_pcc_config_info { + uint32_t bus_type; + uint16_t bus; + uint8_t device; + uint8_t function; + uint8_t config[256]; +}; +struct db_usb_config_info { + uint32_t bus_type; +}; +struct db_iee1394_config_info { + uint32_t bus_type; +}; +struct db_config_info { + union { + struct db_pci_config_info pci; + struct db_pcc_config_info pcc; + struct db_usb_config_info usb; + struct db_iee1394_config_info iee1394; + }; +}; + +struct cpb_initialize { + uint64_t memory_addr; + uint32_t memory_length; + uint32_t link_speed; + uint16_t tx_buf_cnt; + uint16_t tx_buf_size; + uint16_t rx_buf_cnt; + uint16_t rx_buf_size; + uint8_t duplex; + uint8_t loopback; +} PACKED; + +struct db_initialize { + uint32_t memory_used; + uint16_t tx_buf_cnt; + uint16_t tx_buf_size; + uint16_t rx_buf_cnt; + uint16_t rx_buf_size; +} PACKED; + +struct cpb_station_address { + undi_mac_addr station_addr; +} PACKED; + +struct db_station_address { + undi_mac_addr station_address; + undi_mac_addr broadcast_address; + undi_mac_addr permanent_address; +} PACKED; + +struct cpb_receive_filters { + undi_mac_addr mcast_list[UNDI_MAX_MCAST_ADDRESS_CNT]; +} PACKED; + +struct db_receive_filters { + undi_mac_addr mcast_list[UNDI_MAX_MCAST_ADDRESS_CNT]; +} PACKED; + + +struct db_get_status { + uint32_t rx_frame_len; + uint32_t reserved; + uint64_t tx_buffer[UNDI_MAX_XMIT_BUFFERS]; +} PACKED; + +struct cpb_transmit { + uint64_t frame_addr; + uint32_t data_len; + uint16_t media_header_len; + uint16_t reserved; +} PACKED; + +struct cpb_receive { + uint64_t buffer_addr; + uint32_t buffer_len; + uint32_t reserved; +} PACKED; +struct db_receive { + undi_mac_addr src_addr; + undi_mac_addr dest_addr; + uint32_t frame_len; + undi_media_protocol protocol; + uint16_t media_header_len; + undi_frame_type type; + uint8_t reserved[7]; +} PACKED; +struct fptr { + void *func; + void *gp; +}; + +extern char __gp[]; + +/* Variables */ +static unsigned undi_ifnum; +static void *undi_entry_point; +static struct cdb cdb; +static char buffer[1024*1024]; + +/* SW UNDI callbacks */ +static void undi_udelay(uint64_t microseconds) +{ +#if 0 + printf("undi_udelay(%lx)\n", microseconds); +#endif + if (microseconds < 10) { + microseconds = 10; + } + if (microseconds > 1000) { + mdelay(microseconds/1000); + microseconds%=1000; + } + udelay(microseconds); +} +static struct fptr fptr_undi_udelay = { + .func = &undi_udelay, + .gp = &__gp, +}; +static void undi_block(uint32_t enable __unused) +{ +#if 0 + printf("undi_block(%x)\n", + enable); +#endif + return; +} +static struct fptr fptr_undi_block = { + .func = &undi_block, + .gp = &__gp, +}; +static void undi_virt2phys(uint64_t virtual, uint64_t *ptr) +{ +#if 0 + printf("undi_virt2phys(%lx, %lx)\n", + virtual, ptr); +#endif + *ptr = virt_to_phys((void *)virtual); +} +static struct fptr fptr_undi_virt2phys = { + .func = &undi_virt2phys, + .gp = &__gp, +}; +#define UNDI_IO_READ 0 +#define UNDI_IO_WRITE 1 +#define UNDI_MEM_READ 2 +#define UNDI_MEM_WRITE 3 +static void undi_mem_io(uint8_t read_write, uint8_t len, uint64_t port, uint64_t buf_addr) +{ + printf("undi_mem_io(%hhx, %hhx, %lx, %lx)\n", + read_write, len, port, buf_addr); +#if 0 + if ((read_write == UNDI_IO_READ) && (len == 1)) { + uint8_t *buf = (void *)buf_addr; + *buf = inb(port); + } + else if ((read_write == UNDI_IO_READ) && (len == 2)) { + uint16_t *buf = (void *)buf_addr; + *buf = inw(port); + } + else if ((read_write == UNDI_IO_READ) && (len == 4)) { + uint32_t *buf = (void *)buf_addr; + *buf = inl(port); + } + else if ((read_write == UNDI_IO_READ) && (len == 8)) { + uint64_t *buf = (void *)buf_addr; + *buf = inq(port); + } + else if ((read_write == UNDI_IO_WRITE) && (len == 1)) { + uint8_t *buf = (void *)buf_addr; + outb(*buf, port); + } + else if ((read_write == UNDI_IO_WRITE) && (len == 2)) { + uint16_t *buf = (void *)buf_addr; + outw(*buf, port); + } + else if ((read_write == UNDI_IO_WRITE) && (len == 4)) { + uint32_t *buf = (void *)buf_addr; + outl(*buf, port); + } + else if ((read_write == UNDI_IO_WRITE) && (len == 8)) { + uint64_t *buf = (void *)buf_addr; + outq(*buf, port); + } + else if ((read_write == UNDI_MEM_READ) && (len == 1)) { + uint8_t *buf = (void *)buf_addr; + *buf = readb(port); + } + else if ((read_write == UNDI_MEM_READ) && (len == 2)) { + uint16_t *buf = (void *)buf_addr; + *buf = readw(port); + } + else if ((read_write == UNDI_MEM_READ) && (len == 4)) { + uint32_t *buf = (void *)buf_addr; + *buf = readl(port); + } + else if ((read_write == UNDI_MEM_READ) && (len == 8)) { + uint64_t *buf = (void *)buf_addr; + *buf = readq(port); + } + else if ((read_write == UNDI_MEM_WRITE) && (len == 1)) { + uint8_t *buf = (void *)buf_addr; + writeb(*buf, port); + } + else if ((read_write == UNDI_MEM_WRITE) && (len == 2)) { + uint16_t *buf = (void *)buf_addr; + writew(*buf, port); + } + else if ((read_write == UNDI_MEM_WRITE) && (len == 4)) { + uint32_t *buf = (void *)buf_addr; + writel(*buf, port); + } + else if ((read_write == UNDI_MEM_WRITE) && (len == 8)) { + uint64_t *buf = (void *)buf_addr; + writeq(*buf, port); + } +#endif +} +static struct fptr fptr_undi_mem_io = { + .func = &undi_mem_io, + .gp = &__gp, +}; + +/* static void undi_memio(this, width, address, count, buffer);??? */ + + +/* Wrappers to call the undi functions */ +static int undi_call(struct cdb *cdb) +{ + int result = 1; + cdb->stat_code = CDB_STATCODE_INITIALIZE; + cdb->stat_flags = CDB_STATFLAGS_INITIALIZE; + cdb->ifnum = undi_ifnum; + cdb->control = CDB_CONTROL_LAST_CDB_IN_LIST; + __call(undi_entry_point, cdb); + /* Wait until the command executes... */ + while((cdb->stat_flags & CDB_STATFLAGS_STATUS_MASK) == 0) + ; + if ((cdb->stat_flags & CDB_STATFLAGS_STATUS_MASK) != + CDB_STATFLAGS_COMMAND_COMPLETE) + result = 0; + if (cdb->stat_code != CDB_STATCODE_SUCCESS) + result = 0; + return result; +} + +static int get_state(struct cdb *cdb) +{ + memset(cdb, 0, sizeof(*cdb)); + cdb->op_code = CDB_OP_GET_STATE; + cdb->op_flags = CDB_OPFLAGS_NOT_USED; + return undi_call(cdb); +} + +static int start(struct cdb *cdb) +{ + static struct cpb_start cpb; + memset(&cpb, 0, sizeof(cpb)); + cpb.delay = &fptr_undi_udelay; + cpb.block = &fptr_undi_block; + cpb.virt2phys = &fptr_undi_virt2phys; + cpb.mem_io = &fptr_undi_mem_io; + + memset(cdb, 0, sizeof(*cdb)); + cdb->op_code = CDB_OP_START; + cdb->op_flags = CDB_OPFLAGS_NOT_USED; + cdb->cpb_size = sizeof(cpb); + cdb->cpb_addr = virt_to_phys(&cpb); + + return undi_call(cdb); +} +static int stop(struct cdb *cdb) +{ + memset(cdb, 0, sizeof(*cdb)); + cdb->op_code = CDB_OP_STOP; + cdb->op_flags = CDB_OPFLAGS_NOT_USED; + return undi_call(cdb); +} +static int get_init_info(struct cdb *cdb, struct db_init_info *info) +{ + memset(info, 0, sizeof(*info)); + memset(cdb, 0, sizeof(*cdb)); + cdb->op_code = CDB_OP_GET_INIT_INFO; + cdb->op_flags = CDB_OPFLAGS_NOT_USED; + cdb->db_size = sizeof(*info); + cdb->db_addr = virt_to_phys(info); + return undi_call(cdb); +} + +#if 0 +/* get_config_info crashes broadcoms pxe driver */ +static int get_config_info(struct cdb *cdb, struct db_config_info *info) +{ + memset(info, 0, sizeof(*info)); + memset(cdb, 0, sizeof(*cdb)); + cdb->op_code = CDB_OP_GET_CONFIG_INFO; + cdb->op_flags = CDB_OPFLAGS_NOT_USED; + cdb->db_size = sizeof(*info); + cdb->db_addr = virt_to_phys(info); + return undi_call(cdb); +} +#endif +static int initialize(struct cdb *cdb, int media_detect, + struct cpb_initialize *cpb, struct db_initialize *db) +{ + memset(db, 0, sizeof(*db)); + memset(cdb, 0, sizeof(*cdb)); + cdb->op_code = CDB_OP_INITIALIZE; + cdb->op_flags = media_detect? + CDB_OPFLAGS_INIT_DETECT_CABLE:CDB_OPFLAGS_INIT_DO_NOT_DETECT_CABLE; + cdb->cpb_size = sizeof(*cpb); + cdb->cpb_addr = virt_to_phys(cpb); + cdb->db_size = sizeof(*db); + cdb->db_addr = virt_to_phys(db); + return undi_call(cdb); +} +static int shutdown(struct cdb *cdb) +{ + memset(cdb, 0, sizeof(*cdb)); + cdb->op_code = CDB_OP_SHUTDOWN; + cdb->op_flags = CDB_OPFLAGS_NOT_USED; + return undi_call(cdb); +} +static int station_address_read(struct cdb *cdb, struct db_station_address *db) +{ + memset(db, 0, sizeof(*db)); + memset(cdb, 0, sizeof(*cdb)); + cdb->op_code = CDB_OP_STATION_ADDRESS; + cdb->op_flags = CDB_OPFLAGS_STATION_ADDRESS_READ; + cdb->db_size = sizeof(*db); + cdb->db_addr = virt_to_phys(db); + return undi_call(cdb); +} +static int receive_filters(struct cdb *cdb, unsigned opflags) +{ + /* I currently do not support setting + * or returning the multicast filter list. + * So do not even attempt to pass them. + */ + memset(cdb, 0, sizeof(*cdb)); + cdb->op_code = CDB_OP_RECEIVE_FILTERS; + cdb->op_flags = opflags; + return undi_call(cdb); +} +static int get_transmitted_status(struct cdb *cdb, struct db_get_status *db) +{ + memset(db, 0, sizeof(*db)); + memset(cdb, 0, sizeof(*cdb)); + cdb->op_code = CDB_OP_GET_STATUS; + cdb->op_flags = CDB_OPFLAGS_GET_TRANSMITTED_BUFFERS; + cdb->db_size = sizeof(*db); + cdb->db_addr = virt_to_phys(db); + return undi_call(cdb); +} + +static int transmit(struct cdb *cdb, struct cpb_transmit *cpb) +{ + memset(cdb, 0, sizeof(*cdb)); + cdb->op_code = CDB_OP_TRANSMIT; + cdb->op_flags = CDB_OPFLAGS_TRANSMIT_WHOLE | CDB_OPFLAGS_TRANSMIT_DONT_BLOCK; + cdb->cpb_size = sizeof(*cpb); + cdb->cpb_addr = virt_to_phys(cpb); + return undi_call(cdb); +} + +static int receive(struct cdb *cdb, + struct cpb_receive *cpb, struct db_receive *db) +{ + memset(db, 0, sizeof(*db)); + memset(cdb, 0, sizeof(*cdb)); + cdb->op_code = CDB_OP_RECEIVE; + cdb->op_flags = CDB_OPFLAGS_NOT_USED; + cdb->cpb_size = sizeof(*cpb); + cdb->cpb_addr = virt_to_phys(cpb); + cdb->db_size = sizeof(*db); + cdb->db_addr = virt_to_phys(db); + return undi_call(cdb); +} + +/* The work horse functions */ +static int nic_poll(struct nic *nic ) +{ + int result; + struct cpb_receive cpb; + struct db_receive db; + + memset(&cpb, 0, sizeof(cpb)); + cpb.buffer_addr = virt_to_phys(nic->packet); + cpb.buffer_len = ETH_FRAME_LEN; + result = receive(&cdb, &cpb, &db); + if (result) { + nic->packetlen = db.frame_len; + return 1; + } + else if (cdb.stat_code != CDB_STATCODE_NO_DATA) { + printf("Receive failed: %lx\n", cdb.stat_code); + } + return 0; /* initially as this is called to flush the input */ +} + +static void nic_transmit(struct nic *nic, const char *dest, unsigned int type, + unsigned int size, const char *data) +{ + int result; + static struct { + uint8_t dst_addr[ETH_ALEN]; + uint8_t src_addr[ETH_ALEN]; + uint16_t type; + uint8_t data[ETH_MAX_MTU]; + } packet; + struct cpb_transmit cpb; + struct db_get_status db; + int done; + + /* Build the packet to transmit in my buffer */ + memcpy(&packet.dst_addr, dest, ETH_ALEN); + memcpy(&packet.src_addr, nic->node_addr, ETH_ALEN); + packet.type = htons(type); + memcpy(&packet.data, data, size); + + /* send the packet to destination */ + cpb.frame_addr = virt_to_phys(&packet); + cpb.data_len = ETH_HLEN + size; + cpb.media_header_len = ETH_HLEN; + result = transmit(&cdb, &cpb); + if (!result) { + printf("transmit failed: %lx\n", cdb.stat_code); + return; + } + /* Wait until the packet is actually transmitted, + * indicating it is safe to reuse my trasmit buffer. + */ + done = 0; + while(!done) { + int i; + result = get_transmitted_status(&cdb, &db); + for(i = 0; i < UNDI_MAX_XMIT_BUFFERS; i++) { + if (db.tx_buffer[i] == virt_to_phys(&packet)) { + done = 1; + } + } + } +} + +static void nic_disable(struct dev *dev) +{ + struct nic *nic = (struct nic *)dev; + int result; + result = shutdown(&cdb); + if (!result) { + printf("UNDI nic does not want to shutdown: %x\n", cdb.stat_code); + } + result = stop(&cdb); + if (!result) { + printf("UNDI nic does not want to stop: %x\n", cdb.stat_code); + } + undi_ifnum = 0; + undi_entry_point = 0; +} + +static uint8_t undi_checksum(struct sw_undi *undi) +{ + uint8_t undi_sum, *ptr; + int i; + ptr = (uint8_t *)undi; + undi_sum = 0; + for(i = 0; i < undi->len; i++) { + undi_sum += ((char *)undi)[i]; + } + return undi_sum; +} + +#if 0 +/* Debug functions */ +void print_nii(EFI_NETWORK_INTERFACE_IDENTIFIER_INTERFACE *nii) +{ + printf("NII Revision: %lx\n", nii->Revision); + printf("NII ID: %lx\n", nii->ID); + printf("NII ImageAddr: %lx\n", nii->ImageAddr); + printf("NII ImageSize: %x\n", nii->ImageSize); + printf("NII StringID: %c%c%c%c\n", + nii->StringId[0], nii->StringId[1], nii->StringId[2], nii->StringId[3]); + printf("NII Type: %hhx\n", nii->Type); + printf("NII Version: %d.%d\n", nii->MajorVer, nii->MinorVer); + printf("NII IfNum: %hhx\n", nii->IfNum); + printf("\n"); +} +void print_sw_undi(struct sw_undi *undi) +{ + int i; + printf("UNDI signature: %c%c%c%c\n", + undi->signature[0], undi->signature[1], undi->signature[2], undi->signature[3]); + printf("UNDI len: %hhx\n", undi->len); + printf("UNDI fudge: %hhx\n", undi->fudge); + printf("UNDI rev: %hhx\n", undi->rev); + printf("UNDI ifcnt: %hhx\n", undi->ifcnt); + printf("UNDI version: %d.%d\n", undi->major, undi->minor); + printf("UNDI implementation: %x\n", undi->implementation); + printf("UNDI entry point: %lx\n", undi->entry_point); + printf("UNDI bus type cnt: %d\n", undi->bus_type_cnt); + for(i = 0; i < undi->bus_type_cnt; i++) { + printf("UNDI bus type: %c%c%c%c\n", + ((undi->bus_type[i]) >> 0) & 0xff, + ((undi->bus_type[i]) >> 8) & 0xff, + ((undi->bus_type[i]) >> 16) & 0xff, + ((undi->bus_type[i]) >> 24) & 0xff); + } + printf("UNDI sum: %hhx\n", undi_checksum(undi)); + printf("\n"); +} +void print_init_info(struct db_init_info *info) +{ + printf("init_info.memory_required: %d\n", info->memory_required); + printf("init_info.frame_data_len: %d\n", info->frame_data_len); + printf("init_info.link_speeds: %d %d %d %d\n", + info->link_speeds[0], info->link_speeds[1], + info->link_speeds[2], info->link_speeds[3]); + printf("init_info.media_header_len: %d\n", info->media_header_len); + printf("init_info.hw_addr_len: %d\n", info->hw_addr_len); + printf("init_info.mcast_filter_cnt: %d\n", info->mcast_filter_cnt); + printf("init_info.tx_buf_cnt: %d\n", info->tx_buf_cnt); + printf("init_info.tx_buf_size: %d\n", info->tx_buf_size); + printf("init_info.rx_buf_cnt: %d\n", info->rx_buf_cnt); + printf("init_info.rx_buf_size: %d\n", info->rx_buf_size); + printf("init_info.if_type: %hhx\n", info->if_type); + printf("init_info.duplex: %hhx\n", info->duplex); + printf("init_info.loopback: %hhx\n", info->loopback); + printf("\n"); +} +void print_config_info(struct db_config_info *info) +{ + int i; + printf("config_info.bus_type: %c%c%c%c\n", + ((info->pci.bus_type) >> 0) & 0xff, + ((info->pci.bus_type) >> 8) & 0xff, + ((info->pci.bus_type) >> 16) & 0xff, + ((info->pci.bus_type) >> 24) & 0xff); + if (info->pci.bus_type != UNDI_BUS_TYPE_PCI) { + return; + } + printf("config_info.bus: %hx\n", info->pci.bus); + printf("config_info.device: %hhx\n", info->pci.device); + printf("config_info.function: %hhx\n", info->pci.function); + printf("config_info.config:\n"); + for(i = 0; i < 256; i++) { + if ((i & 0xf) == 0) { + printf("[%hhx]", i); + } + printf(" %hhx", info->pci.config[i]); + if ((i & 0xf) == 0xf) { + printf("\n"); + } + } + printf("\n"); + +} +void print_cdb(struct cdb *cdb) +{ + printf("\n"); + printf("cdb.op_code: %hx\n", cdb->op_code); + printf("cdb.op_flags: %hx\n", cdb->op_flags); + printf("cdb.cpb_size: %d\n", cdb->cpb_size); + printf("cdb.db_size: %d\n", cdb->db_size); + printf("cdb.cpb_addr: %lx\n", cdb->cpb_addr); + printf("cdb.db_addr: %lx\n", cdb->db_addr); + printf("cdb.stat_code: %lx\n", cdb->stat_code); + printf("cdb.stat_flags: %lx\n", cdb->stat_flags); + printf("cdb.ifnum %d\n", cdb->ifnum); + printf("cdb.control: %hx\n", cdb->control); + printf("\n"); +} +#endif +#define ARPHRD_ETHER 1 +static int nic_setup(struct dev *dev, + EFI_NETWORK_INTERFACE_IDENTIFIER_INTERFACE *nii) +{ + struct nic *nic = (struct nic *)dev; + int result; + struct sw_undi *undi; + struct db_init_info init_info; + struct cpb_initialize cpb_initialize; + struct db_initialize db_initialize; + struct db_station_address db_station_address; + int media_detect; + unsigned filter, no_filter; + int i; + + /* Fail if I I'm not passed a valid nii */ + if (!nii) + return 0; + + /* Fail if this nit a SW UNDI interface */ + if (nii->ID == 0) + return 0; + + undi = phys_to_virt(nii->ID); + + /* Verify the undi structure */ + + /* It must have a pxe signature */ + if (memcmp(undi->signature, "!PXE", 4) != 0) + return 0; + /* It must have a valid checksum */ + if (undi_checksum(undi) != 0) + return 0; + /* It must be software undi */ + if (undi->implementation & UNDI_IMP_HW_UNDI) + return 0; + + /* Setup to do undi calls */ + undi_ifnum = nii->IfNum; + undi_entry_point = (void *)undi->entry_point; + + /* Find the UNDI state... */ + result = get_state(&cdb); + if (!result) + return 0; + + /* See if the device is already initialized */ + if ((cdb.stat_flags & CDB_STATFLAGS_GET_STATE_MASK) != + CDB_STATFLAGS_GET_STATE_STOPPED) { + + /* If so attempt to stop it */ + if ((cdb.stat_flags & CDB_STATFLAGS_GET_STATE_MASK) == + CDB_STATFLAGS_GET_STATE_INITIALIZED) { + result = shutdown(&cdb); + result = stop(&cdb); + } + else if ((cdb.stat_flags & CDB_STATFLAGS_GET_STATE_MASK) == + CDB_STATFLAGS_GET_STATE_STARTED) { + result = stop(&cdb); + } + + /* See if it did stop */ + result = get_state(&cdb); + if (!result) + return 0; + + /* If it didn't stop give up */ + if ((cdb.stat_flags & CDB_STATFLAGS_GET_STATE_MASK) != + CDB_STATFLAGS_GET_STATE_STOPPED) + return 0; + + } + + result = start(&cdb); + if (!result) { + printf("Device would not start: %x\n", cdb.stat_code); + return 0; + } + result = get_init_info(&cdb, &init_info); + if (!result) { + printf("Device wount not give init info: %x\n", cdb.stat_code); + stop(&cdb); + return 0; + } + /* See if the NIC can detect the presence of a cable */ + media_detect = (cdb.stat_flags & CDB_STATFLAGS_CABLE_DETECT_MASK) == + CDB_STATFLAGS_CABLE_DETECT_SUPPORTED; + + if ((init_info.if_type != ARPHRD_ETHER) || + (init_info.hw_addr_len != ETH_ALEN)) { + printf("Not ethernet\n"); + stop(&cdb); + return 0; + } + if (init_info.memory_required > sizeof(buffer)) { + printf("NIC wants %d bytes I only have %ld bytes\n", + init_info.memory_required, sizeof(buffer)); + stop(&cdb); + return 0; + } + /* Initialize the device */ + memset(buffer, 0, sizeof(buffer)); + memset(&cpb_initialize, 0, sizeof(cpb_initialize)); + cpb_initialize.memory_addr = virt_to_phys(&buffer); + cpb_initialize.memory_length = init_info.memory_required; + cpb_initialize.link_speed = 0; /* auto detect */ + /* UNDI nics will not take suggestions :( + * So let them figure out an appropriate buffer stragety on their own. + */ + cpb_initialize.tx_buf_cnt = 0; + cpb_initialize.tx_buf_size = 0; + cpb_initialize.rx_buf_cnt = 0; + cpb_initialize.rx_buf_size = 0; + cpb_initialize.duplex = 0; + cpb_initialize.loopback = 0; + result = initialize(&cdb, media_detect, &cpb_initialize, &db_initialize); + if (!result) { + printf("Device would not initialize: %x\n", cdb.stat_code); + stop(&cdb); + return 0; + } +#if 0 + /* It appears the memory_used parameter is never set correctly, ignore it */ + if (db_initialize.memory_used > sizeof(buffer)) { + printf("NIC is using %d bytes I only have %ld bytes\n", + db_initialize.memory_used, sizeof(buffer)); + printf("tx_buf_cnt: %d\n", db_initialize.tx_buf_cnt); + printf("tx_buf_size: %d\n", db_initialize.tx_buf_size); + printf("rx_buf_cnt: %d\n", db_initialize.rx_buf_cnt); + printf("rx_buf_size: %d\n", db_initialize.rx_buf_size); + nic_disable(dev); + return 0; + } + printf("NIC is using %d bytes\n", + db_initialize.memory_used); +#endif + if (media_detect && ( + (cdb.stat_flags & ~CDB_STATFLAGS_STATUS_MASK) == + CDB_STATFLAGS_INITIALIZED_NO_MEDIA)) { + printf("No media present\n"); + nic_disable(dev); + return 0; + } + + /* Get the mac address */ + result = station_address_read(&cdb, &db_station_address); + if (!result) { + printf("Could not read station address: %x\n", + cdb.stat_code); + nic_disable(dev); + return 0; + } + for(i = 0; i < ETH_ALEN; i++) { + nic->node_addr[i] = db_station_address.station_address[i]; + } + printf("Ethernet addr: %!\n", nic->node_addr); + + filter = CDB_OPFLAGS_RECEIVE_FILTER_ENABLE | + CDB_OPFLAGS_RECEIVE_FILTER_UNICAST | + CDB_OPFLAGS_RECEIVE_FILTER_BROADCAST; + no_filter = CDB_OPFLAGS_RECEIVE_FILTER_DISABLE | + CDB_OPFLAGS_RECEIVE_FILTER_RESET_MCAST_LIST | + CDB_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST; + + if (undi->implementation & UNDI_IMP_PROMISCUOUS_MULTICAST_RX_SUPPORTED) { + filter |= CDB_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST; + no_filter |= CDB_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS; + } + else if (undi->implementation & UNDI_IMP_PROMISCUOUS_RX_SUPPORTED) { + filter |= CDB_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS; + } + + result = receive_filters(&cdb, no_filter); + if (!result) { + printf("Could not clear receive filters: %x\n", + cdb.stat_code); + nic_disable(dev); + return 0; + } + result = receive_filters(&cdb, filter); + if (!result) { + printf("Could not set receive filters: %x\n", + cdb.stat_code); + nic_disable(dev); + return 0; + } + + /* It would be nice to call get_config_info so I could pass + * the type of nic, but that crashes some efi drivers. + */ + /* Everything worked! */ + dev->disable = nic_disable; + nic->poll = nic_poll; + nic->transmit = nic_transmit; + + return 1; +} + +/************************************************************************** +PROBE - Look for an adapter, this routine's visible to the outside +***************************************************************************/ +static int nic_probe(struct dev *dev, unsigned short *dummy __unused) +{ + EFI_NETWORK_INTERFACE_IDENTIFIER_INTERFACE *nii; + int index; + int result; + + index = dev->index+ 1; + if (dev->how_probe == PROBE_AWAKE) { + index--; + } + for(result = 0; !result && (nii = lookup_efi_nic(index)); index++) { + result = nic_setup(dev, nii); + if (result) { + break; + } + } + dev->index = result ? index : -1; + return result; +} + + + + +static struct isa_driver nic_driver __isa_driver = { + .type = NIC_DRIVER, + .name = "undi_nii", + .probe = nic_probe, + .ioaddrs = 0, +}; diff --git a/src/arch/ia64/include/bits/byteswap.h b/src/arch/ia64/include/bits/byteswap.h new file mode 100644 index 00000000..a8a11585 --- /dev/null +++ b/src/arch/ia64/include/bits/byteswap.h @@ -0,0 +1,36 @@ +#ifndef ETHERBOOT_BITS_BYTESWAP_H +#define ETHERBOOT_BITS_BYTESWAP_H + +static inline uint64_t __ia64_bswap_64(uint64_t x) +{ + uint64_t result; + __asm__ volatile( + "mux1 %0=%1,@rev" : + "=r" (result) + : "r" (x)); + return result; +} + +#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_16(x) \ + (__builtin_constant_p(x) ? \ + __bswap_constant_16(x) : \ + (__ia64_bswap_64(x) >> 48)) + + +#define __bswap_32(x) \ + (__builtin_constant_p(x) ? \ + __bswap_constant_32(x) : \ + (__ia64_bswap_64(x) >> 32)) + + +#endif /* ETHERBOOT_BITS_BYTESWAP_H */ diff --git a/src/arch/ia64/include/bits/cpu.h b/src/arch/ia64/include/bits/cpu.h new file mode 100644 index 00000000..d8fe1cbb --- /dev/null +++ b/src/arch/ia64/include/bits/cpu.h @@ -0,0 +1,6 @@ +#ifndef IA64_BITS_CPU_H +#define IA64_BITS_CPU_H + +#define cpu_setup() do {} while(0) + +#endif /* IA64_BITS_CPU_H */ diff --git a/src/arch/ia64/include/bits/elf.h b/src/arch/ia64/include/bits/elf.h new file mode 100644 index 00000000..c68f8456 --- /dev/null +++ b/src/arch/ia64/include/bits/elf.h @@ -0,0 +1,11 @@ +#ifndef IA64_BITS_ELF_H +#define IA64_BITS_ELF_H + +/* ELF Defines for the current architecture */ +#define EM_CURRENT EM_IA_64 +#define ELFDATA_CURRENT ELFDATA2LSB + +#define ELF_CHECK_ARCH(x) \ + ((x).e_machine == EM_CURRENT) + +#endif /* IA64_BITS_ELF_H */ diff --git a/src/arch/ia64/include/bits/endian.h b/src/arch/ia64/include/bits/endian.h new file mode 100644 index 00000000..413e702d --- /dev/null +++ b/src/arch/ia64/include/bits/endian.h @@ -0,0 +1,6 @@ +#ifndef ETHERBOOT_BITS_ENDIAN_H +#define ETHERBOOT_BITS_ENDIAN_H + +#define __BYTE_ORDER __LITTLE_ENDIAN + +#endif /* ETHERBOOT_BITS_ENDIAN_H */ diff --git a/src/arch/ia64/include/bits/string.h b/src/arch/ia64/include/bits/string.h new file mode 100644 index 00000000..31e94b7d --- /dev/null +++ b/src/arch/ia64/include/bits/string.h @@ -0,0 +1,6 @@ +#ifndef ETHERBOOT_BITS_STRING_H +#define ETHERBOOT_BITS_STRING_H + +/* define inline optimized string functions here */ + +#endif /* ETHERBOOT_BITS_STRING_H */ diff --git a/src/arch/ia64/include/hooks.h b/src/arch/ia64/include/hooks.h new file mode 100644 index 00000000..d8f1f06a --- /dev/null +++ b/src/arch/ia64/include/hooks.h @@ -0,0 +1,12 @@ +#ifndef ETHERBOOT_IA64_HOOKS_H +#define ETHERBOOT_IA64_HOOKS_H + +#include + +void arch_main(in_call_data_t *data, va_list params); +void arch_on_exit(int status); +void arch_relocate_to(unsigned long addr); +#define arch_relocated_from(old_addr) do {} while(0) + + +#endif /* ETHERBOOT_IA64_HOOKS_H */ diff --git a/src/arch/ia64/include/io.h b/src/arch/ia64/include/io.h new file mode 100644 index 00000000..be5a5ce1 --- /dev/null +++ b/src/arch/ia64/include/io.h @@ -0,0 +1,228 @@ +#ifndef ETHERBOOT_IO_H +#define ETHERBOOT_IO_H + +/* Don't require identity mapped physical memory, + * osloader.c is the only valid user at the moment. + */ +static inline unsigned long virt_to_phys(volatile const void *virt_addr) +{ + return ((unsigned long)virt_addr); +} + +static inline void *phys_to_virt(unsigned long phys_addr) +{ + return (void *)(phys_addr); +} + +/* virt_to_bus converts an addresss inside of etherboot [_start, _end] + * into a memory address 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; +} + +/* In physical mode the offset of uncached pages */ +#define PHYS_BASE (0x8000000000000000UL) + +/* Memory mapped IO primitives, we avoid the cache... */ +static inline uint8_t readb(unsigned long addr) +{ + return *((volatile uint8_t *)(PHYS_BASE | addr)); +} + +static inline uint16_t readw(unsigned long addr) +{ + return *((volatile uint16_t *)(PHYS_BASE | addr)); +} + +static inline uint32_t readl(unsigned long addr) +{ + return *((volatile uint32_t *)(PHYS_BASE | addr)); +} + +static inline uint64_t readq(unsigned long addr) +{ + return *((volatile uint64_t *)(PHYS_BASE | addr)); +} + + +static inline void writeb(uint8_t val, unsigned long addr) +{ + *((volatile uint8_t *)(PHYS_BASE | addr)) = val; +} + +static inline void writew(uint16_t val, unsigned long addr) +{ + *((volatile uint16_t *)(PHYS_BASE | addr)) = val; +} + +static inline void writel(uint32_t val, unsigned long addr) +{ + *((volatile uint32_t *)(PHYS_BASE | addr)) = val; +} + +static inline void writeq(uint64_t val, unsigned long addr) +{ + *((volatile uint64_t *)(PHYS_BASE | addr)) = val; +} + + +static inline void memcpy_fromio(void *dest, unsigned long src, size_t n) +{ + size_t i; + uint8_t *dp = dest; + for(i = 0; i < n; i++) { + *dp = readb(src); + dp++; + src++; + } +} + +static inline void memcpy_toio(unsigned long dest , const void *src, size_t n) +{ + size_t i; + const uint8_t *sp = src; + for(i = 0; i < n; i++) { + writeb(*sp, dest); + sp++; + dest++; + } +} + +/* IO space IO primitives, Itanium has a strange architectural mapping... */ +extern unsigned long io_base; +#define __ia64_mf_a() __asm__ __volatile__ ("mf.a" ::: "memory") +#define __ia64_io_addr(port) ((void *)(PHYS_BASE | io_base | (((port) >> 2) << 12) | ((port) & 0xfff))) + +static inline uint8_t inb(unsigned long port) +{ + uint8_t result; + + result = *((volatile uint8_t *)__ia64_io_addr(port)); + __ia64_mf_a(); + return result; +} + +static inline uint16_t inw(unsigned long port) +{ + uint8_t result; + result = *((volatile uint16_t *)__ia64_io_addr(port)); + __ia64_mf_a(); + return result; +} + +static inline uint32_t inl(unsigned long port) +{ + uint32_t result; + result = *((volatile uint32_t *)__ia64_io_addr(port)); + __ia64_mf_a(); + return result; +} + +static inline void outb(uint8_t val, unsigned long port) +{ + *((volatile uint8_t *)__ia64_io_addr(port)) = val; + __ia64_mf_a(); +} + +static inline void outw(uint16_t val, unsigned long port) +{ + *((volatile uint16_t *)__ia64_io_addr(port)) = val; + __ia64_mf_a(); +} + +static inline void outl(uint32_t val, unsigned long port) +{ + *((volatile uint32_t *)__ia64_io_addr(port)) = val; + __ia64_mf_a(); +} + + + +static inline void insb(unsigned long port, void *dst, unsigned long count) +{ + volatile uint8_t *addr = __ia64_io_addr(port); + uint8_t *dp = dst; + __ia64_mf_a(); + while(count--) + *dp++ = *addr; + __ia64_mf_a(); +} + +static inline void insw(unsigned long port, void *dst, unsigned long count) +{ + volatile uint16_t *addr = __ia64_io_addr(port); + uint16_t *dp = dst; + __ia64_mf_a(); + while(count--) + *dp++ = *addr; + __ia64_mf_a(); +} + +static inline void insl(unsigned long port, void *dst, unsigned long count) +{ + volatile uint32_t *addr = __ia64_io_addr(port); + uint32_t *dp = dst; + __ia64_mf_a(); + while(count--) + *dp++ = *addr; + __ia64_mf_a(); +} + +static inline void outsb(unsigned long port, void *src, unsigned long count) +{ + const uint8_t *sp = src; + volatile uint8_t *addr = __ia64_io_addr(port); + + while (count--) + *addr = *sp++; + __ia64_mf_a(); +} + +static inline void outsw(unsigned long port, void *src, unsigned long count) +{ + const uint16_t *sp = src; + volatile uint16_t *addr = __ia64_io_addr(port); + + while (count--) + *addr = *sp++; + __ia64_mf_a(); +} + +static inline void outsl(unsigned long port, void *src, unsigned long count) +{ + const uint32_t *sp = src; + volatile uint32_t *addr = __ia64_io_addr(port); + + while (count--) + *addr = *sp++; + __ia64_mf_a(); +} + +static inline unsigned long ia64_get_kr0(void) +{ + unsigned long r; + asm volatile ("mov %0=ar.k0" : "=r"(r)); + return r; +} + +#endif /* ETHERBOOT_IO_H */ diff --git a/src/arch/ia64/include/latch.h b/src/arch/ia64/include/latch.h new file mode 100644 index 00000000..87195b42 --- /dev/null +++ b/src/arch/ia64/include/latch.h @@ -0,0 +1,11 @@ +#ifndef LATCH_H +#define LATCH_H + +#define TICKS_PER_SEC (1000UL) + +/* Fixed timer interval used for calibrating a more precise timer */ +#define LATCHES_PER_SEC 10 + +void sleep_latch(void); + +#endif /* LATCH_H */ diff --git a/src/arch/ia64/include/limits.h b/src/arch/ia64/include/limits.h new file mode 100644 index 00000000..0c6f21f9 --- /dev/null +++ b/src/arch/ia64/include/limits.h @@ -0,0 +1,57 @@ +#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_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 long' can hold */ +#define LONG_MAX 9223372036854775807L +#define LONG_MIN (-LONG_MAX - 1L) + +/* Maximum value an `unsigned long' can hold. (Minimum is 0.) */ +#define ULONG_MAX 18446744073709551615UL + +/* 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/src/arch/ia64/include/pal.h b/src/arch/ia64/include/pal.h new file mode 100644 index 00000000..6cda19d1 --- /dev/null +++ b/src/arch/ia64/include/pal.h @@ -0,0 +1,11 @@ +#ifndef IA64_PAL_H +#define IA64_PAL_H + +struct pal_freq_ratio { + unsigned long den : 32, num : 32; /* numerator & denominator */ +}; +extern long pal_freq_ratios(struct pal_freq_ratio *proc_ratio, + struct pal_freq_ratio *bus_ratio, struct pal_freq_ratio *itc_ratio); + + +#endif /* IA64_PAL_H */ diff --git a/src/arch/ia64/include/sal.h b/src/arch/ia64/include/sal.h new file mode 100644 index 00000000..7a1b57ec --- /dev/null +++ b/src/arch/ia64/include/sal.h @@ -0,0 +1,29 @@ +#ifndef IA64_SAL_H +#define IA64_SAL_H + +struct fptr { + unsigned long entry; + unsigned long gp; +}; +extern struct fptr sal_entry; +extern struct fptr pal_entry; +extern int parse_sal_system_table(void *table); + +#define SAL_FREQ_BASE_PLATFORM 0 +#define SAL_FREQ_BASE_INTERVAL_TIMER 1 +#define SAL_FREQ_BASE_REALTIME_CLOCK 2 + +long sal_freq_base (unsigned long which, unsigned long *ticks_per_second, + unsigned long *drift_info); + +#define PCI_SAL_ADDRESS(seg, bus, dev, fn, reg) \ + ((unsigned long)(seg << 24) | (unsigned long)(bus << 16) | \ + (unsigned long)(dev << 11) | (unsigned long)(fn << 8) | \ + (unsigned long)(reg)) + +long sal_pci_config_read ( + unsigned long pci_config_addr, unsigned long size, unsigned long *value); +long sal_pci_config_write ( + unsigned long pci_config_addr, unsigned long size, unsigned long value); + +#endif /* IA64_SAL_H */ diff --git a/src/arch/ia64/include/setjmp.h b/src/arch/ia64/include/setjmp.h new file mode 100644 index 00000000..a1fac2dc --- /dev/null +++ b/src/arch/ia64/include/setjmp.h @@ -0,0 +1,13 @@ +#ifndef ETHERBOOT_SETJMP_H +#define ETHERBOOT_SETJMP_H + + +/* Define a type for use by setjmp and longjmp */ +#define JBLEN 70 + +typedef long jmp_buf[JBLEN] __attribute__ ((aligned (16))); /* guarantees 128-bit alignment! */ + +extern int setjmp (jmp_buf env); +extern void longjmp (jmp_buf env, int val); + +#endif /* ETHERBOOT_SETJMP_H */ diff --git a/src/arch/ia64/include/stdint.h b/src/arch/ia64/include/stdint.h new file mode 100644 index 00000000..2f9c592c --- /dev/null +++ b/src/arch/ia64/include/stdint.h @@ -0,0 +1,16 @@ +#ifndef STDINT_H +#define STDINT_H + +typedef unsigned long size_t; + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long uint64_t; + +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed int int32_t; +typedef signed long int64_t; + +#endif /* STDINT_H */ diff --git a/src/arch/ia64/prefix/apply_efi_prefix.pl b/src/arch/ia64/prefix/apply_efi_prefix.pl new file mode 100755 index 00000000..999c43b4 --- /dev/null +++ b/src/arch/ia64/prefix/apply_efi_prefix.pl @@ -0,0 +1,63 @@ +#!/usr/bin/perl -w +# +# Program to apply an efi header to an ia64 etherboot file. +# +# GPL Eric Biederman 2002 +# + +use strict; + +use bytes; + +main(@ARGV); + +sub usage +{ + my ($err) = @_; + print STDERR $err , "\n"; + die "Usage $0 prrefix file bss_size\n"; +} +sub main +{ + my ($prefix_name, $suffix_name, $bss_size) = @_; + usage("No prefix") unless (defined($prefix_name)); + usage("No suffix") unless (defined($suffix_name)); + usage("No bss size") unless (defined($bss_size)); + + open(PREFIX, "<$prefix_name") or die "Cannot open $prefix_name"; + open(SUFFIX, "<$suffix_name") or die "Cannot open $suffix_name"; + + $/ = undef; + my $prefix = ; close(PREFIX); + my $suffix = ; close(SUFFIX); + + # Payload sizes. + my $payload_size = length($suffix); + my $payload_bss = $bss_size; + + # Update the image size + my $hdr_off = unpack("V",substr($prefix, 0x3c, 4)); + my $image_size_off = 0x050 + $hdr_off; + my $img_mem_size_off = 0x0c0 + $hdr_off; + my $img_size_off = 0x0c8 + $hdr_off; + + my $image_size = unpack("V", substr($prefix, $image_size_off, 4)); + my $img_mem_size = unpack("V", substr($prefix, $img_mem_size_off, 4)); + my $img_size = unpack("V", substr($prefix, $img_size_off, 4)); + + $image_size += $payload_size + $payload_bss; + $img_mem_size += $payload_size + $payload_bss; + $img_size += $payload_size; + + substr($prefix, $image_size_off, 4) = pack("V", $image_size); + substr($prefix, $img_mem_size_off, 4) = pack("V", $img_mem_size); + substr($prefix, $img_size_off, 4) = pack("V", $img_size); + + #print(STDERR "image_size: $image_size\n"); + #print(STDERR "img_mem_size: $img_mem_size\n"); + #print(STDERR "img_size: $img_size\n"); + + print $prefix; + print $suffix; +} + diff --git a/src/arch/ia64/prefix/apply_unnrv2b_prefix.pl b/src/arch/ia64/prefix/apply_unnrv2b_prefix.pl new file mode 100644 index 00000000..71b2b247 --- /dev/null +++ b/src/arch/ia64/prefix/apply_unnrv2b_prefix.pl @@ -0,0 +1,198 @@ +#!/usr/bin/perl -w +# +# Program to apply an unnrv2b decompressor header to an ia64 etherboot file. +# +# GPL Eric Biederman 2002 +# + +use strict; + +use bytes; + +main(@ARGV); + +sub usage +{ + my ($err) = @_; + print STDERR $err , "\n"; + die "Usage $0 prefix file\n"; +} + +sub getbits +{ + my ($bundle, $start, $size) = @_; + + # Compute the mask + my $mask = 0xffffffff; + $mask = $mask >> (32 - $size); + + # Compute the substring, and shift + my ($first, $end, $count, $shift); + $first = int($start / 8); + $end = int(($start + $size + 7)/8); + $count = $end - $first; + $shift = $start % 8; + + # Compute the unpack type + my $type; + if ($count == 1) { + $type = "C" + } + elsif ($count == 2) { + $type = "v"; + } + elsif (($count >= 3) && ($count <= 4)) { + $type = "V"; + } + else { + die "bad count $count"; + } + + # Now compute the value + my $val = (unpack($type, substr($bundle, $first, $count)) >> $shift) & $mask; + + # Now return the value + return $val; +} + +sub putbits +{ + my ($bundle, $start, $size, $val) = @_; + + + # Compute the mask + my $mask = 0xffffffff; + $mask >>= 32 - $size; + + # Compute the substring, and shift + my ($first, $end, $count, $shift); + $first = int($start / 8); + $end = int(($start + $size + 7)/8); + $count = $end - $first; + $shift = $start % 8; + + # Compute the unpack type + my $type; + if ($count == 1) { + $type = "C" + } + elsif ($count == 2) { + $type = "v"; + } + elsif (($count >= 3) && ($count <= 4)) { + $type = "V"; + } + else { + die "bad count $count"; + } + + # Adjust the mask + $mask <<= $shift; + + # Now set the value, preserving the untouched bits + substr($bundle, $first, $count) = + pack($type, + ((unpack($type, substr($bundle, $first, $count)) & ~$mask) | + (($val << $shift) & $mask))); + + # Now return the new value; + return $bundle; +} + +sub main +{ + my ($prefix_name, $suffix_name) = @_; + usage("No prefix") unless (defined($prefix_name)); + usage("No suffix") unless (defined($suffix_name)); + + open(PREFIX, "<$prefix_name") or die "Cannot open $prefix_name"; + open(SUFFIX, "<$suffix_name") or die "Cannot open $suffix_name"; + + $/ = undef; + my $prefix = ; close(PREFIX); + my $suffix = ; close(SUFFIX); + + # Payload sizes + my $prefix_len = length($prefix); + my $suffix_len = length($suffix); + my $payload_size = $suffix_len; + my $uncompressed_offset = ($prefix_len + $suffix_len + 15) & ~15; + my $pad = $uncompressed_offset - ($prefix_len + $suffix_len); + + # Itaninum instruction bundle we will be updating + # 0 - 4 template == 5 + # 5 - 45 slot 0 M-Unit + # 46 - 86 slot 1 L-Unit + # 87 - 127 slot 2 X-Unit + # Itaninum instruction format + # 40 - 37 Major opcode + # ... + # + + # slot 1 + # 0 - 40 [41] imm-41 + # 10 - 40 [31] imm-41-hi + # 0 - 9 [10] imm-41-lo + + # slot 2 + # 0 - 5 [6] qp + # 6 - 12 [7] r1 + # 13 - 19 [7] imm-7b + # 20 [1] vc + # 21 [1] immc + # 22 - 26 [5] imm-5c + # 27 - 35 [9] imm-9d + # 36 [1] imm0 + # 37 - 40 [4] major opcode + # + + # major opcode should be 6 + + # Update the image size + my $uncompressed_offset_bundle_off = 16; + my $bundle = substr($prefix, $uncompressed_offset_bundle_off, 16); + + my $template = getbits($bundle, 0, 5); + my $op1_base = 46; + my $op2_base = 87; + my $major_opcode = getbits($bundle, 37 + $op2_base, 4); + + if (($template != 5) || + ($major_opcode != 6)) { + die "unknown second bundle cannot patch"; + } + + die "uncompressed_offset to big!\n" if ($uncompressed_offset > 0xffffffff); + my $immhi = 0; + my $immlo = $uncompressed_offset; + + my $imm0 = ($immhi >> 31) & ((1 << 1) - 1); + my $imm41_hi = ($immhi >> 0) & ((1 << 31) - 1); + + my $imm41_lo = ($immlo >> 22) & ((1 << 10) - 1); + my $immc = ($immlo >> 21) & ((1 << 1) - 1); + my $imm5c = ($immlo >> 16) & ((1 << 5) - 1); + my $imm9d = ($immlo >> 7) & ((1 << 9) - 1); + my $imm7b = ($immlo >> 0) & ((1 << 7) - 1); + + $bundle = putbits($bundle, 10 + $op1_base, 31, $imm41_hi); + $bundle = putbits($bundle, 0 + $op1_base, 10, $imm41_lo); + $bundle = putbits($bundle, 36 + $op2_base, 1 , $imm0); + $bundle = putbits($bundle, 27 + $op2_base, 9 , $imm9d); + $bundle = putbits($bundle, 22 + $op2_base, 5 , $imm5c); + $bundle = putbits($bundle, 21 + $op2_base, 1 , $immc); + $bundle = putbits($bundle, 13 + $op2_base, 7 , $imm7b); + + substr($prefix, $uncompressed_offset_bundle_off, 16) = $bundle; + + #print (STDERR "prefix: $prefix_len\n"); + #print (STDERR "suffix: $suffix_len\n"); + #print (STDERR "pad: $pad\n"); + #print (STDERR "uncompressed_offset: $uncompressed_offset\n"); + + print $prefix; + print $suffix; + # Pad the resulting image by a few extra bytes... + print pack("C", 0) x $pad; +} + diff --git a/src/arch/ia64/prefix/efi_prefix.S b/src/arch/ia64/prefix/efi_prefix.S new file mode 100644 index 00000000..1fb1501f --- /dev/null +++ b/src/arch/ia64/prefix/efi_prefix.S @@ -0,0 +1,195 @@ +#include "elf.h" + .explicit + + .section ".hdrs", "a" + /* First the DOS file header */ + + +dos_header: + .byte 'M', 'Z' /* Signature */ + .short dos_program_end - dos_program /* Length of image mod 512 bytes*/ + .short 0x0001 /* Length of image in 512 byte pages */ /* FIXME */ + .short 0x0000 /* Number of relocation items following header */ + .short (dos_header_end - dos_header)/16 /* Size of header in 16 byte paragraphs */ + .short 0x0001 /* Minimum number of paragraphs needed to run image */ + .short 0x0001 /* Maximum number of paragraphs program would like */ + .short 0x0000 /* Initial SS */ + + .short 0x00b8 /* Initial SP */ + .short 0x0000 /* Negative checksum of image */ + .short 0x0000 /* Initial IP */ + .short 0x0000 /* Initial CS */ + .short 0x0010 /* Offset in EXEC of first relocation item */ + .short 0x0000 /* Overlay number */ + + .balign 16 +dos_header_end: +dos_program: + .byte 0xdb /* retf */ + .balign 16 +dos_program_end: + .org 0x3c + .byte pe_signature - dos_header, 0x00, 0x00, 0x00 + .org 0x40 /* NOTE: set this to 0x80 for debugging, 0x40 otherwise */ +pe_signature: + .byte 'P', 'E', 0, 0 +coff_header: +#define IMAGE_MACHINE_IA64 0x0200 +coff_machine: .short IMAGE_MACHINE_IA64 +coff_nsections: .short (section_headers_end - section_headers)/40 +coff_timdat: .int 1038168747 /* Sun Nov 24 12:12:27 2002 */ +coff_symptr: .int 0x00000000 + +coff_nsyms: .int 0x00000000 +coff_opthdr: .short pe_end - pe_header +#define CF_RELOC_STRIPPED 0x0001 +#define CF_EXECUTABLE 0x0002 +#define CF_LINE_STRIPPED 0x0004 +#define CF_LOCAL_STRIPPED 0x0008 +#define CF_DEBUG_STRIPPED 0x0206 +coff_flags: .short CF_EXECUTABLE | CF_LINE_STRIPPED | CF_LOCAL_STRIPPED | CF_DEBUG_STRIPPED + + /* Option header */ +pe_header: +pe_magic: .short 0x020b /* 020b or 010b? */ +pe_linker: .byte 0x02, 0x38 +pe_text_size: .int _text_size +pe_data_size: .int _data_size +pe_bss_size: .int _bss_size +pe_entry: .int _start_plabel_rva +pe_text_base: .int _text_rva +pe_image_base: .quad _image_base +pe_sec_align: .int _sect_align +pe_file_align: .int _file_align +pe_os_major: .short 0 +pe_os_minor: .short 0 +pe_image_major: .short 0 +pe_image_minro: .short 0 +pe_sub_major: .short 0 +pe_sub_minor: .short 0 +pe_reserved: .int 0 +pe_image_size: .int _image_size +pe_hdrs_size: .int _hdrs_size +pe_checksum: .int 0 /* FIXME how do I compute the checksum, unnecessary */ +#define SUBSYS_EFI_APP 10 +#define SUBSYS_EFI_BOOT_SERVICE_DRIVER 11 +#define SUBSYS_EFI_RUNTIME_DRIVER 12 +pe_subsys: .short SUBSYS_EFI_APP +pe_dll_flags: .short 0 +pe_stack_res: .quad 0 +pe_stack_commit:.quad 0 +pe_heap_res: .quad 0 +pe_heap_commit: .quad 0 +pe_ld_flags: .int 0 +pe_rvas: .int (rvas_end - rvas_start)/8 + +rvas_start: +rva_0_rva: .int 0 +rva_0_size: .int 0 +rva_1_rva: .int 0 +rva_1_size: .int 0 +rva_2_rva: .int 0 +rva_2_size: .int 0 +rva_3_rva: .int 0 +rva_3_size: .int 0 +rva_4_rva: .int 0 +rva_4_size: .int 0 +rva_5_rva: .int _reloc_rva +rva_5_size: .int __reloc_size +rvas_end: +pe_end: + +section_headers: +#define SCN_CNT_CODE 0x00000020 +#define SCN_CNT_INITIALIZED_DATA 0x00000040 +#define SCN_CNT_UNINITIALIZED_DATA 0x00000080 +#define SCN_MEM_DISCARDABLE 0x02000000 +#define SCN_MEM_SHARED 0x10000000 +#define SCN_MEM_EXECUTE 0x20000000 +#define SCN_MEM_READ 0x40000000 +#define SCN_MEM_WRITE 0x80000000 + +sec1_name: .byte '.', 'i', 'm', 'g', 0 , 0, 0, 0 +sec1_virt_size: .int _img_mem_size +sec1_virt_addr: .int _img_rva +sec1_file_size: .int _img_size +sec1_file_off: .int _img_off +sec1_reloc_off: .int 0 +sec1_line_off: .int 0 +sec1_reloc_cnt: .short 0 +sec1_line_cnt: .short 0 +sec1_flags: .int SCN_CNT_CODE | SCN_CNT_INITIALIZED_DATA \ + | SCN_MEM_EXECUTE | SCN_MEM_READ | SCN_MEM_WRITE + +section_headers_end: + + .text + .psr abi64 + .psr lsb + .global _start +_start: + { + alloc r8=ar.pfs,2,0,0,0 + mov gp=ip /* Get the address of _start/_text/__gp */ + } + ;; + add r14=@gprel(n1_desc),gp + add r15=@gprel(n2_desc),gp + add r16=@gprel(bhdr),gp + ;; + st8 [r14]=in0 + st8 [r15]=in1 + ;; + mov ar.pfs=r8 + ;; + mov r32=r16 + ;; + br.sptk.few _payload_start + + .data + .global _start_plabel + .balign 16 +_start_plabel: + .quad _start + .quad 0 /* I don't need a gp value... */ + + /* hand-crafted bhdr and parameters */ + .balign 16 +bhdr: +b_signature: .int 0x0E1FB007 +b_size: .int bhdr_end - bhdr +b_checksum: .short 0 +b_records: .short 3 + /* A NOP note to 64bit align later data */ + .balign 4 +n0_namesz: .int 0 +n0_descsz: .int 0 +n0_type: .int EBN_NOP + .balign 4 +n1_namesz: .int 10 +n1_descsz: .int 8 +n1_type: .int EB_IA64_IMAGE_HANDLE +n1_name: .asciz "Etherboot" + .balign 4 +n1_desc: .quad 0 + .balign 4 +n2_namesz: .int 10 +n2_descsz: .int 8 +n2_type: .int EB_IA64_SYSTAB +n2_name: .asciz "Etherboot" + .balign 4 +n2_desc: .quad 0 +bhdr_end: + + + /* hand-craft a .reloc section for the plabel */ +#define IMAGE_REL_BASED_ABS 0 +#define IMAGE_REL_BASED_DIR64 10 + + .section ".reloc", "a" + .int _start_plabel_rva // PAGE RVA + .int 12 // Block Size (2*4+2*2) + .short (IMAGE_REL_BASED_DIR64<<12) + 0 // reloc for plabel's entry point + .short (IMAGE_REL_BASED_ABS <<12) + 0 // dummy reloc for good alignment + + diff --git a/src/arch/ia64/prefix/efi_prefix.lds b/src/arch/ia64/prefix/efi_prefix.lds new file mode 100644 index 00000000..aa71ea2e --- /dev/null +++ b/src/arch/ia64/prefix/efi_prefix.lds @@ -0,0 +1,91 @@ +/* OUTPUT_FORMAT("binary") */ +OUTPUT_FORMAT("elf64-ia64-little") + +OUTPUT_ARCH(ia64) + +ENTRY(_start_plabel) +_sect_align = 16; /* normally 512 */ +_file_align = 16; /* normally 4096 */ +/* Symbols for hardcoding the payload and _bss size, with apply_efi_prefix + * there is no need to set these, and in will get confused if these are not 0. + */ +_payload_size = 0; +_payload_bss = 0; +SECTIONS { + /* We can arbitrarily set image base to anything we want, + * but efi does not honor it, so it is a pointless exercise. + * So we just set the start address to 0. + */ + . = 0; + _link_base = . ; + _image_base = . ; + .hdrs : { + _hdrs = . ; + *(.hdrs) + . = ALIGN(_file_align) ; + _ehdrs = . ; + } + . = ALIGN(_sect_align); + .img : { + _img = . ; + _text = . ; + __gp = . ; + *(.text) + _etext = .; + . = ALIGN(16); + _data = . ; + *(.data) + _edata = .; + . = ALIGN(16); + _reloc = . ; + *(.reloc) + __ereloc = . ; + _ereloc = . ; + . = ALIGN(16); + /* . = ALIGN(_file_align) ; */ + } + _payload_start = . ; + . = . + _payload_size ; + _payload_end = . ; + _eimg = . ; + . = ALIGN(_sect_align) ; + _bss = . ; + .bss : { + *(.bss) + . = . + _payload_bss; + } + _ebss = . ; + _end = . ; + /DISCARD/ : { + *(*) + } + + _hdrs_size = _ehdrs - _hdrs; + _hdrs_off = 0; + + _text_size = _etext - _text ; + _text_rva = _text - _image_base ; + _text_off = _text - _link_base; + + _data_size = _edata - _data ; + _data_rva = _data - _image_base; + _data_off = _data - _link_base; + + __reloc_size = __ereloc - _reloc ; + _reloc_size = _ereloc - _reloc ; + _reloc_rva = _reloc - _image_base; + _reloc_off = _reloc - _link_base; + + _bss_size = _ebss - _bss; + _bss_rva = _bss - _image_base; + + _img_size = _eimg - _img ; + _img_rva = _img - _image_base; + _img_off = _img - _link_base; + + _img_mem_size = _ebss - _img; + + _image_size = _ebss - _link_base ; + + _start_plabel_rva = _start_plabel - _image_base; +} diff --git a/src/arch/ia64/prefix/unnrv2b.S b/src/arch/ia64/prefix/unnrv2b.S new file mode 100644 index 00000000..bd7013b2 --- /dev/null +++ b/src/arch/ia64/prefix/unnrv2b.S @@ -0,0 +1,196 @@ +/* + * Copyright (C) 1996-2002 Markus Franz Xaver Johannes Oberhumer + * Copyright (C) 2002 Eric Biederman + * + * 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 + * + * - Converted to functional ia64 assembly (Can this get smaller?) + * Eric Biederman 5 Dec 2002 + */ + .text + .globl _start +_start: + /* See where I am running, and compute gp */ + { + /* Do no call alloc here as I do not know how many argument + * registers are being passed through the decompressor, and if I report + * to few the unreported registers may get stomped. + * + * Instead just explicitly get the value of ar.pfs. + */ + mov r17=0 + mov r8=ar.pfs + mov gp = ip /* The linker scripts sets gp at _start */ + + } + {.mlx + movl r9=0x123456789abcdef0 /* Get uncompressed_offset into r9 */ + } + ;; + { + add r14 = @gprel(payload + 4),gp + add r15 = r9,gp + mov r16=1 /* last_m_off = 1 */ + } + { + mov r20 = 0xd00 + add r21 = r9,gp + br.sptk.few decompr_loop_n2b + } + +/* ------------- DECOMPRESSION ------------- + + Input: + r8 - ar.pfs + r14 - source + r15 - dest + r16 - 1 + r17 - (buffer) 0 + r20 - 0xd00 (constant) + r21 - start address + Usage: + r9 - scratch register for memory copies + r18 - scratch register for getbit + r19 - scratch register for loads and stores + Output: + r2 - 0 + r3 - 0 +*/ + +getbit: + add r18 = r17,r17 + ;; + cmp.ne p8,p0 = r0,r18 + cmp.leu p6,p7 = r18,r17 + ;; + mov r17 = r18 +(p8) br.cond.sptk.few getbit_end + /* Do a unaligned 64bit load */ + ;; + ld1 r17 = [r14],1 + ;; + ld1 r18 = [r14],1 + ;; + dep r17 = r18,r17,8,8 + ld1 r18 = [r14],1 + ;; + dep r17 = r18,r17,16,8 + ld1 r18 = [r14],1 + ;; + dep r17 = r18,r17,24,8 + ld1 r18 = [r14],1 + ;; + dep r17 = r18,r17,32,8 + ld1 r18 = [r14],1 + ;; + dep r17 = r18,r17,40,8 + ld1 r18 = [r14],1 + ;; + dep r17 = r18,r17,48,8 + ld1 r18 = [r14],1 + ;; + dep r17 = r18,r17,56,8 + ;; + add r18 = r17,r17,1 + ;; + cmp.leu p6,p7=r18,r17 + ;; + mov r17=r18 + ;; +getbit_end: + br.ret.sptk.few b6 + + +decompr_literals_n2b: + ld1 r19 = [r14],1 + ;; + st1 [r15] = r19,1 + ;; +decompr_loop_n2b: + br.call.sptk.few b6 = getbit + ;; +(p6) br.cond.sptk.few decompr_literals_n2b +(p7) add r2 = 1,r0 /* m_off = 1 */ + ;; +loop1_n2b: + br.call.sptk.few b6 = getbit + ;; +(p6) add r2 = r2,r2,1 /* m_off = m_off*2 + getbit() */ +(p7) add r2 = r2,r2 + br.call.sptk.few b6 = getbit + ;; +(p7) br.cond.sptk.few loop1_n2b /* while(!getbit()) */ + ;; + mov r3 = r0 + cmp.eq p6,p0 = 2,r2 + add r2 = -3,r2 +(p6) br.cond.sptk.few decompr_ebpeax_n2b /* if (m_off == 2) goto decompr_ebpeax_n2b ? */ + ;; + ld1 r19 = [r14],1 + shl r2 = r2,8 + ;; + dep r2 = r19,r2,0,8 /* m_off = (m_off - 3)*256 + src[ilen++] */ + ;; + cmp4.eq p6,p0 = -1,r2 /* if (m_off == 0xffffffff) goto decomp_end_n2b */ + ;; +(p6) br.cond.sptk.few decompr_end_n2b + mov r16 = r2 /* last_m_off = m_off */ + ;; +decompr_ebpeax_n2b: + br.call.sptk.few b6 = getbit + ;; +(p6) add r3 = r3,r3,1 /* m_len = getbit() */ +(p7) add r3 = r3,r3 + br.call.sptk.few b6 = getbit + ;; +(p6) add r3 = r3,r3,1 /* m_len = m_len*2 + getbit()) */ +(p7) add r3 = r3,r3 + ;; + cmp.ne p6,p0 = r0,r3 +(p6) br.cond.sptk.few decompr_got_mlen_n2b /* if (m_len == 0) goto decompr_got_mlen_n2b */ + add r3 = 1,r3 /* m_len++ */ + ;; +loop2_n2b: + br.call.sptk.few b6 = getbit + ;; +(p6) add r3 = r3,r3,1 /* m_len = m_len*2 + getbit() */ +(p7) add r3 = r3,r3 + br.call.sptk.few b6 = getbit + ;; +(p7) br.cond.sptk.few loop2_n2b /* while(!getbit()) */ + add r3 = 2, r3 /* m_len += 2 */ + ;; +decompr_got_mlen_n2b: + cmp.gtu p6,p7 = r16, r20 + ;; +(p6) add r3 = 2, r3 /* m_len = m_len + 1 + (last_m_off > 0xd00) */ +(p7) add r3 = 1, r3 + sub r9 = r15, r16,1 /* m_pos = dst + olen - last_m_off - 1 */ + ;; +1: + ld1 r19 = [r9],1 + add r3 = -1,r3 + ;; + st1 [r15] = r19,1 /* dst[olen++] = *m_pos++ while(m_len > 0) */ + cmp.ne p6,p0 = r0,r3 +(p6) br.cond.sptk.few 1b + ;; + br.cond.sptk.few decompr_loop_n2b +decompr_end_n2b: + /* Branch to the start address */ + mov ar.pfs=r8 + ;; + mov b6 = r21 + ;; + br.sptk.few b6 + +payload: diff --git a/src/arch/ia64/prefix/unnrv2b.lds b/src/arch/ia64/prefix/unnrv2b.lds new file mode 100644 index 00000000..728f7009 --- /dev/null +++ b/src/arch/ia64/prefix/unnrv2b.lds @@ -0,0 +1,28 @@ +OUTPUT_FORMAT("elf64-ia64-little") + +OUTPUT_ARCH(ia64) + +ENTRY(_start) +SECTIONS { + . = 0; + __gp = .; + _text = . ; + .text : { + *(.text) + } + /DISCARD/ : { + *(.comment) + *(.note) + *(.hash) + *(.data) + *(.sbss) + *(.bss) + *(.dynstr) + *(.dynsym) + *(.IA_64.unwind) + *(.IA_64.unwind_info) + *(.IA64_unwind) + *(.IA64_unwind_info) + *(.dynamic) + } +} diff --git a/src/core/btext.c b/src/core/btext.c new file mode 100644 index 00000000..12afb8d5 --- /dev/null +++ b/src/core/btext.c @@ -0,0 +1,5196 @@ +#ifdef CONSOLE_BTEXT +#ifdef CONFIG_PCI +/* + * Procedures for drawing on the screen early on in the boot process. + * + * Benjamin Herrenschmidt + * + * move to LinuxBIOS by LYH yhlu@tyan.com + * move to Etherboot by LYH + */ + +#include "etherboot.h" +#include "pci.h" + +#ifdef CONFIG_FILO +#include +#endif + +#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(unsigned char c, u32 locX, u32 locY); +#if 0 +static void draw_byte_32(unsigned char *bits, u32 *base, u32 rb); +static void draw_byte_16(unsigned char *bits, u32 *base, u32 rb); +#endif +static void draw_byte_8(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 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 + */ +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 = (unsigned char *)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. + */ + +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; +#if 1 + base = bi->logicalDisplayBase; + if (base == 0) +#endif + base = bi->dispDeviceBase; + base += (x + bi->dispDeviceRect[0]) * (bi->dispDeviceDepth >> 3); + base += (y + bi->dispDeviceRect[1]) * bi->dispDeviceRowBytes; + return base; +} + + +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)); +} + +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 */ + +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 +void BTEXT +btext_drawstring(const char *c) +{ + if (!boot_text_mapped) + return; + while (*c) + btext_drawchar(*c++); +} +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(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 char *font = &vga_font[(((u32)c)) * 16]; +#else + unsigned 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(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(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(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); + } +} + +#ifdef CONFIG_FILO +#define USE_FILO_PCI_FIND 1 +#else +#define USE_FILO_PCI_FIND 0 +#endif + + +void btext_init(void) +{ +#if 0 +// for debug +#define frame_buffer 0xfc000000 +#else + uint32_t frame_buffer;// 0xfc000000 + + struct pci_device *dev = 0; + +#if USE_FILO_PCI_FIND==0 + pci_find_device_x(0x1002, 0x4752, 0, &dev); + if(dev.vendor==0) return; // no fb + + frame_buffer = (uint32_t)dev.membase; +#else + pci_init(); + dev = pci_find_device(0x1002, 0x4752, -1, -1, 0); + if(!dev) { + return; // no fb + } + + pci_read_config_dword(dev, 0x10, &frame_buffer); +#endif + +#endif + + btext_setup_display(640, 480, 8, 640,frame_buffer); +// btext_clearscreen(); //move to main +// map_boot_text(); //move console_init +} +void btext_putc(int c) +{ + btext_drawchar((unsigned char)c); +} +#if 0 +static struct console_driver btext_console __console = { + .init = btext_init, + .tx_byte = btext_tx_byte, + .rx_byte = 0, + .tst_byte = 0, +}; +#endif +#if USE_FILO_PCI_FIND==0 +int pci_find_device_x(int vendorx, int devicex, int index, struct pci_device *dev) +{ + unsigned int first_bus, first_devfn; + unsigned int devfn, bus, buses; +#if 1 + unsigned char hdr_type = 0; +#endif + uint32_t class; + uint16_t vendor, device; + uint32_t l, membase; +#if 0 + uint32_t ioaddr, romaddr; + int reg; +#endif + + first_bus = 0; + first_devfn = 0; +#if 0 + if(dev->vendor!=0){ + first_bus = dev->bus; + first_devfn = dev->devfn; + /* Re read the header type on a restart */ + pcibios_read_config_byte(first_bus, first_devfn & ~0x7, + PCI_HEADER_TYPE, &hdr_type); + dev->bus = 0; + dev->devfn = 0; + } +#endif + + /* Scan all PCI buses, until we find our card. + * We could be smart only scan the required buses but that + * is error prone, and tricky. + * By scanning all possible pci buses in order we should find + * our card eventually. + */ + buses=256; + for (bus = first_bus; bus < buses; ++bus) { + for (devfn = first_devfn; devfn < 0xff; ++devfn) { +#if 1 + if (PCI_FUNC (devfn) == 0) + pcibios_read_config_byte(bus, devfn, PCI_HEADER_TYPE, &hdr_type); + else if (!(hdr_type & 0x80)) /* not a multi-function device */ + continue; +#endif + pcibios_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l); + /* some broken boards return 0 if a slot is empty: */ + if (l == 0xffffffff || l == 0x00000000) { + continue; + } + vendor = l & 0xffff; + device = (l >> 16) & 0xffff; +#if 0 + pcibios_read_config_dword(bus, devfn, PCI_REVISION, &l); + class = (l >> 8) & 0xffffff; +#endif + +#if 0 + { + int i; + printf("%hhx:%hhx.%hhx [%hX/%hX]\n", + bus, PCI_SLOT(devfn), PCI_FUNC(devfn), + vendor, device); +#if 0 + for(i = 0; i < 256; i++) { + unsigned char byte; + if ((i & 0xf) == 0) { + printf("%hhx: ", i); + } + pcibios_read_config_byte(bus, devfn, i, &byte); + printf("%hhx ", byte); + if ((i & 0xf) == 0xf) { + printf("\n"); + } + } +#endif + + } +#endif + if(vendor != vendorx) continue; + + if(device != devicex) continue; + + if(index !=0 ) { + index--; + continue; + } + + + dev->devfn = devfn; + dev->bus = bus; +#if 0 + dev->class = class; +#endif + dev->vendor = vendor; + dev->dev_id = device; + +#if 0 + /* Get the ROM base address */ + pcibios_read_config_dword(bus, devfn, + PCI_ROM_ADDRESS, &romaddr); + romaddr >>= 10; + dev->romaddr = romaddr; +#endif + + /* Get the ``membase'' */ + pcibios_read_config_dword(bus, devfn, + PCI_BASE_ADDRESS_0, &membase); + dev->membase = membase; +#if 0 + /* Get the ``ioaddr'' */ + for (reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4) { + pcibios_read_config_dword(bus, devfn, reg, &ioaddr); + if ((ioaddr & PCI_BASE_ADDRESS_IO_MASK) == 0 || (ioaddr & PCI_BASE_ADDRESS_SPACE_IO) == 0) + continue; + + + /* Strip the I/O address out of the returned value */ + ioaddr &= PCI_BASE_ADDRESS_IO_MASK; + + /* Take the first one or the one that matches in boot ROM address */ + dev->ioaddr = ioaddr; + } +#endif +#if 0 + printf("Found %s ROM address %#hx\n", + dev->name, romaddr); +#endif + return 1; + } + first_devfn = 0; + } + first_bus = 0; + return 0; +} +#endif +//come from linux/drivers/video/font-8x16.c +/**********************************************/ +/* */ +/* Font file generated by cpi2fnt */ +/* */ +/**********************************************/ + + +static 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 +#endif diff --git a/src/core/config.c b/src/core/config.c new file mode 100644 index 00000000..180b0669 --- /dev/null +++ b/src/core/config.c @@ -0,0 +1,161 @@ +/* + * 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" +#ifdef BUILD_SERIAL +#include ".buildserial.h" +#define xstr(s) str(s) +#define str(s) #s +#endif + +void print_config(void) +{ + printf( "Etherboot " VERSION +#ifdef BUILD_SERIAL + " [build " +#ifdef BUILD_ID + BUILD_ID " " +#endif + "#" xstr(BUILD_SERIAL_NUM) "]" +#endif /* BUILD_SERIAL */ + " (GPL) http://etherboot.org\n" + "Drivers: " ); +#ifdef CONFIG_PCI + pci_enumerate(); +#endif +#ifdef CONFIG_ISA + isa_enumerate(); +#endif + printf( " Images: " +#ifdef TAGGED_IMAGE + "NBI " +#endif +#ifdef ELF64_IMAGE + "ELF64 " +#endif +#ifdef ELF_IMAGE + "ELF " +#endif +#ifdef COFF_IMAGE + "COFF " +#endif +#ifdef IMAGE_FREEBSD + "FreeBSD " +#endif +#ifdef IMAGE_MULTIBOOT + "Multiboot " +#endif +#ifdef AOUT_IMAGE + "a.out " +#endif +#ifdef WINCE_IMAGE + "WINCE " +#endif +#ifdef PXE_IMAGE + "PXE " +#endif +#ifdef PXE_EXPORT /* All possible exports */ + " Exports: " +#ifdef PXE_EXPORT + "PXE " +#endif +#endif /* All possible exports */ + " " + ); +#if (BOOTP_SERVER != 67) || (BOOTP_CLIENT != 68) + printf( "[DHCP ports %d and %d] ", + BOOTP_SERVER, BOOTP_CLIENT); +#endif + putchar('\n'); + printf( "Protocols: " +#ifdef RARP_NOT_BOOTP + "RARP " +#else +# ifndef NO_DHCP_SUPPORT + "DHCP " +# else + "BOOTP " +# endif +#endif +#ifdef DOWNLOAD_PROTO_TFTP + "TFTP " +#endif +#ifdef DOWNLOAD_PROTO_NFS + "NFS " +#endif +#ifdef DOWNLOAD_PROTO_SLAM + "SLAM " +#endif +#ifdef DOWNLOAD_PROTO_TFTM + "TFTM " +#endif +#ifdef DOWNLOAD_PROTO_HTTP + "HTTP " +#endif +#ifdef PROTO_LACP + "LACP " +#endif +#ifdef DNS_RESOLVER + "DNS " +#endif + "\n"); +} + +static const char *driver_name[] = { + "nic", + "disk", + "floppy", +}; + +int probe(struct dev *dev) +{ + const char *type_name; + type_name = ""; + if ((dev->type >= 0) && + ((unsigned)dev->type < sizeof(driver_name)/sizeof(driver_name[0]))) { + type_name = driver_name[dev->type]; + } + if (dev->how_probe == PROBE_FIRST) { + dev->to_probe = PROBE_PCI; + memset(&dev->state, 0, sizeof(dev->state)); + } + if (dev->to_probe == PROBE_PCI) { +#ifdef CONFIG_PCI + dev->how_probe = pci_probe(dev, type_name); +#else + dev->how_probe = PROBE_FAILED; +#endif + if (dev->how_probe == PROBE_FAILED) { + dev->to_probe = PROBE_ISA; + } + } + if (dev->to_probe == PROBE_ISA) { +#ifdef CONFIG_ISA + dev->how_probe = isa_probe(dev, type_name); +#else + dev->how_probe = PROBE_FAILED; +#endif + if (dev->how_probe == PROBE_FAILED) { + dev->to_probe = PROBE_NONE; + } + } + if ((dev->to_probe != PROBE_PCI) && + (dev->to_probe != PROBE_ISA)) { + dev->how_probe = PROBE_FAILED; + + } + return dev->how_probe; +} + +void disable(struct dev *dev) +{ + if (dev->disable) { + dev->disable(dev); + dev->disable = 0; + } +} diff --git a/src/core/disk.c b/src/core/disk.c new file mode 100644 index 00000000..2d31b627 --- /dev/null +++ b/src/core/disk.c @@ -0,0 +1,283 @@ +#include "etherboot.h" +#include "disk.h" + +#undef disk_disable + +static int dummy(void *unused __unused) +{ + return (0); +} + +static unsigned char disk_buffer[DISK_BUFFER_SIZE]; +struct disk disk = +{ + { + 0, /* dev.disable */ + { + 0, + 0, + PCI_BUS_TYPE, + }, /* dev.devid */ + 0, /* index */ + 0, /* type */ + PROBE_FIRST, /* how_probe */ + PROBE_NONE, /* to_probe */ + 0, /* failsafe */ + 0, /* type_index */ + {}, /* state */ + }, + (int (*)(struct disk *, sector_t ))dummy, /* read */ + 0 - 1, /* drive */ + 0, /* hw_sector_size */ + 0, /* sectors_per_read */ + 0, /* bytes */ + 0, /* sectors */ + 0, /* sector */ + disk_buffer, /* buffer */ + 0, /* priv */ + + 0, /* disk_offset */ + 0, /* direction */ +}; + + +static int disk_read( + struct disk *disk, unsigned char *buffer, sector_t sector) +{ + int result; + sector_t base_sector; + + /* Note: I do not handle disk wrap around here! */ + + /* Compute the start of the track cache */ + base_sector = sector; + /* Support sectors_per_read > 1 only on small disks */ + if ((sizeof(sector_t) > sizeof(unsigned long)) && + (disk->sectors_per_read > 1)) { + unsigned long offset; + offset = ((unsigned long)sector) % disk->sectors_per_read; + base_sector -= offset; + } + + /* See if I need to update the track cache */ + if ((sector < disk->sector) || + sector >= disk->sector + (disk->bytes >> 9)) { + twiddle(); + result = disk->read(disk, base_sector); + if (result < 0) + return result; + } + /* Service the request from the track cache */ + memcpy(buffer, disk->buffer + ((sector - base_sector)<<9), SECTOR_SIZE); + return 0; +} + +static int disk_read_sectors( + struct disk *disk, + unsigned char *buffer, + sector_t base_sector, unsigned int sectors) +{ + sector_t sector = 0; + unsigned long offset; + int result = 0; + + for(offset = 0; offset < sectors; offset++) { + sector = base_sector + offset; + if (sector >= disk->sectors) { + sector -= disk->sectors; + } + result = disk_read(disk, buffer + (offset << 9), sector); + if (result < 0) + break; + } + if (result < 0) { + printf("disk read error at 0x%lx\n", sector); + } + return result; +} + +static os_download_t probe_buffer(unsigned char *buffer, unsigned int len, + int increment, unsigned int offset, unsigned int *roffset) +{ + os_download_t os_download; + unsigned int end; + end = 0; + os_download = 0; + if (increment > 0) { + end = len - SECTOR_SIZE; + } + do { + offset += increment; + os_download = probe_image(buffer + offset, len - offset); + } while(!os_download && (offset != end)); + *roffset = offset; + return os_download; +} + +static int load_image( + struct disk *disk, + unsigned char *buffer, unsigned int buf_sectors, + sector_t block, unsigned int offset, + os_download_t os_download) +{ + sector_t skip_sectors; + + skip_sectors = 0; + while(1) { + skip_sectors = os_download(buffer + offset, + (buf_sectors << 9) - offset, 0); + + block += skip_sectors + buf_sectors; + if (block >= disk->sectors) { + block -= disk->sectors; + } + + offset = 0; + buf_sectors = 1; + if (disk_read_sectors(disk, buffer, block, 1) < 0) { + return 0; + } + } + return -1; +} + +int disk_probe(struct dev *dev) +{ + struct disk *disk = (struct disk *)dev; + if (dev->how_probe == PROBE_NEXT) { + disk->drive += 1; + } + return probe(dev); +} + + +int disk_load_configuration(struct dev *dev) +{ + /* Start with the very simplest possible disk configuration */ + struct disk *disk = (struct disk *)dev; + disk->direction = (dev->failsafe)?-1:1; + disk->disk_offset = 0; + return 0; +} + +int disk_load(struct dev *dev) +{ + struct disk *disk = (struct disk *)dev; + /* 16K == 8K in either direction from the start of the disk */ + static unsigned char buffer[32*SECTOR_SIZE]; + os_download_t os_download; + unsigned int offset; + unsigned int len; + unsigned int buf_sectors; + volatile sector_t block; + volatile int inc, increment; + int i; + int result; + jmp_buf real_restart; + + + printf("Searching for image...\n"); + result = 0; + /* Only check for 16byte aligned images */ + increment = (disk->direction < 0)?-16:16; + /* Load a buffer, and see if it contains the start of an image + * we can boot from disk. + */ + len = sizeof(buffer); + buf_sectors = sizeof(buffer) / SECTOR_SIZE; + inc = increment; + block = (disk->disk_offset) >> 9; + if (buf_sectors/2 > block) { + block = (disk->sectors - (buf_sectors/2)) + block; + } + /* let probe buffer assume offset always needs to be incremented */ + offset = (len/2 + ((disk->disk_offset) & 0x1ff)) - inc; + + /* Catch longjmp so if this image fails to load, I start looking + * for the next image where I left off looking for this image. + */ + memcpy(&real_restart, &restart_etherboot, sizeof(jmp_buf)); + i = setjmp(restart_etherboot); + if ((i != 0) && (i != -2)) { + memcpy(&restart_etherboot, &real_restart, sizeof(jmp_buf)); + longjmp(restart_etherboot, i); + } + /* Read the canidate sectors into the buffer */ + if (disk_read_sectors(disk, buffer, block, buf_sectors) < 0) { + result = -1; + goto out; + } + if (inc == increment) { + os_download = probe_buffer(buffer, len, inc, offset, &offset); + if (os_download) + goto load_image; + inc = -inc; + } + os_download = probe_buffer(buffer, len, inc, offset, &offset); + if (!os_download) { + result = -1; + goto out; + } + load_image: + printf("Loading image...\n"); + result = load_image(disk, buffer, buf_sectors, block, offset, os_download); + out: + memcpy(&restart_etherboot, &real_restart, sizeof(jmp_buf)); + return result; +} + +int url_file(const char *name, + int (*fnc)(unsigned char *, unsigned int, unsigned int, int) __unused) +{ + unsigned int drive; + unsigned long disk_offset; + int direction; + int type; + + disk_offset = 0; + direction = 1; + if (memcmp(name, "disk", 4) == 0) { + type = DISK_DRIVER; + name += 4; + } + else if (memcmp(name, "floppy", 6) == 0) { + type = FLOPPY_DRIVER; + name += 6; + } + else { + printf("Unknown device type\n"); + return 0; + } + drive = strtoul(name, &name, 10); + if ((name[0] == '+') || (name[0] == '-')) { + direction = (name[0] == '-')? -1 : 1; + name++; + disk_offset = strtoul(name, &name, 10); + } + if (name[0]) { + printf("Junk '%s' at end of disk url\n", name); + return 0; + } + memset(&disk, 0, sizeof(disk)); + disk.buffer = disk_buffer; + disk.drive = 0; + disk.dev.how_probe = PROBE_FIRST; + disk.dev.type = type; + do { + disk_disable(); + disk.dev.how_probe = disk_probe(&disk.dev); + if (disk.dev.how_probe == PROBE_FAILED) { + printf("Not that many drives\n"); + return 0; + } + } while(disk.drive < drive); + disk.direction = direction; + disk.disk_offset = disk_offset; + + return disk_load(&disk.dev); +} + +void disk_disable(void) +{ + disable(&disk.dev); +} diff --git a/src/core/dns_resolver.c b/src/core/dns_resolver.c new file mode 100644 index 00000000..52f24a4d --- /dev/null +++ b/src/core/dns_resolver.c @@ -0,0 +1,419 @@ +/************************************************************************** +* +* dns_resolver.c: Etherboot support for resolution of host/domain +* names in filename parameters +* Written 2004 by Anselm M. Hoffmeister +* +* +* 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 code is using nuts and bolts from throughout etherboot. +* It is a fresh implementation according to the DNS RFC, #1035 +* +* $Revision$ +* $Author$ +* $Date$ +* +* REVISION HISTORY: +* ================ +* 2004-05-10 File created +* 2004-05-19 First release to CVS +* 2004-05-22 CNAME support first stage finished +* 2004-05-24 First "stable" release to CVS +* 2004-08-28 Improve readability, set recursion flag +***************************************************************************/ + +#ifdef DNS_RESOLVER +#include "etherboot.h" +#include "nic.h" +#include "dns_resolver.h" + +#define MAX_DNS_RETRIES 3 +#define MAX_CNAME_RECURSION 0x30 +#undef DNSDEBUG + +int donameresolution ( char * hostname, int hnlength, char * deststring ); + +/* + * dns_resolver + * Function: Main function for name resolution - will be called by other + * parts of etherboot + * Param: string filename (not containing proto prefix like "tftp://") + * Return: string filename, with hostname replaced by IP in dotted quad + * or NULL for resolver error + * In case no substitution is necessary, return will be = param + * The returned string, if not == input, will be temporary and + * probably be overwritten during the next call to dns_resolver + * The returned string may (or may not) only contain an IP + * address, or an IP followed by a ":" or "/", or be NULL(=error) + */ +char * dns_resolver ( char * filename ) { + int i = 0, j, k; + static char ipaddr[16] = { 0 }; + // Search for "end of hostname" (which might be either ":" or "/") + for ( j = i; (filename[j] != ':') && (filename[j] != '/'); ++j ) { + // If no hostname delimiter was found, assume no name present + if ( filename[j] == 0 ) return filename; + } + // Check if the filename is an IP, in which case, leave unchanged + k = j - i - 1; + while ( ( '.' == filename[i+k] ) || + ( ( '0' <= filename[i+k] ) && ( '9' >= filename[i+k] ) ) ) { + --k; + if ( k < 0 ) return filename; // Only had nums and dots->IP + } + // Now that we know it's a full hostname, attempt to resolve + if ( donameresolution ( filename + i, j - i, ipaddr ) ) { + return NULL; // Error in resolving - Fatal. + } + // Return the dotted-quad IP which resulted + return ipaddr; +} + +/* + * await_dns + * Shall be called on any incoming packet during the resolution process + * (as is the case with all the other await_ functions in etherboot) + * Param: as any await functions + * Return: see dns_resolver.h for constant return values + descriptions + */ +static int await_dns (int ival, void *ptr, + unsigned short ptype __unused, struct iphdr *ip __unused, + struct udphdr *udp, struct tcphdr *tcp __unused) { + int i, j, k; + unsigned char *p = (unsigned char *)udp + sizeof(struct udphdr); + // p is set to the beginning of the payload + unsigned char *q; + unsigned int querytype = QUERYTYPE_A; + if ( 0 == udp ) // Parser couldn't find UDP header + return RET_PACK_GARBAG; // Not a UDP packet + if (( UDP_PORT_DNS != ntohs (udp->src )) || + ( UDP_PORT_DNS != ntohs (udp->dest)) ) + // Neither source nor destination port is "53" + return RET_PACK_GARBAG; // UDP port wrong + if (( p[QINDEX_ID ] != 0) || + ( p[QINDEX_ID+1] != (ival & 0xff))) + // Checking if this packet has set (inside payload) + // the sequence identifier that we expect + return RET_PACK_GARBAG; // Not matching our request ID + if (( p[QINDEX_FLAGS ] & QUERYFLAGS_MASK ) != QUERYFLAGS_WANT ) + // We only accept responses to the query(ies) we sent + return RET_PACK_GARBAG; // Is not response=opcode <0> + querytype = (ival & 0xff00) >> 8; + if (((p[QINDEX_NUMANSW+1] + (p[QINDEX_NUMANSW+1]<<8)) == 0 ) || + ( ERR_NOSUCHNAME == (p[QINDEX_FLAGS+1] & 0x0f) ) ) { + // Answer section has 0 entries, or "no such name" returned + if ( QUERYTYPE_A == querytype) { + // It was an A type query, so we should try if there's + // an alternative "CNAME" record available + return RET_RUN_CNAME_Q; // So try CNAME query next + } else if ( QUERYTYPE_CNAME == querytype) { + // There's no CNAME either, so give up + return RET_NOSUCHNAME; + } else { + // Anything else? No idea what. Should not happen. + return RET_NOSUCHNAME; // Bail out with error + } + } + if ( 0 != ( p[QINDEX_FLAGS+1] & 0x0f ) ) + // The response packet's flag section tells us: + return RET_NOSUCHNAME; // Another (unspecific) error occured + // Now we have an answer packet in response to our query. Next thing + // to do is to search the payload for the "answer section", as there is + // a question section first usually that needs to be skipped + // If question section was not repeated, that saves a lot of work : + if ( 0 >= (i = ((p[QINDEX_NUMQUEST] << 8) + p[QINDEX_NUMQUEST+1]))) { + q = p+ QINDEX_QUESTION; // No question section, just continue; + } else if ( i >= 2 ) { // More than one query section? Error! + return RET_NOSUCHNAME; // That's invalid for us anyway - + // We only place one query at a time + } else { + // We have to skip through the question section first to + // find the beginning of the answer section + q = p + QINDEX_QUESTION; + while ( 0 != q[0] ) + q += q[0] + 1; // Skip through + q += 5; // Skip over end-\0 and query type section + } + // Now our pointer shows the beginning of the answer section + // So now move it past the (repeated) query string, we only + // want the answer + while ( 0 != q[0] ) { + if ( 0xc0 == ( q[0] & 0xc0 ) ) { // Pointer + ++q; + break; + } + q += q[0] + 1; + } + ++q; + // Now check wether it's an INET host address (resp. CNAME)? + // There seem to be nameservers out there (Bind 9, for example), + // that return CNAMEs when no As are there, e.g. in + // testname.test. IN CNAME othername.test. + // case, bind 9 would return the CNAME record on an A query. + // Accept this case as it saves a lot of work (an extra query) + if (( QUERYTYPE_CNAME == q[1] ) && + ( QUERYTYPE_A == querytype )) { + // A query, but CNAME response. + // Do simulate having done a CNAME query now and just assume + // the answer we are working on here is that of a CNAME query + // This works for single-depth CNAMEs fine. + // For deeper recursion, we won't parse the answer + // packet deeper but rely on a separate CNAME query + querytype = QUERYTYPE_CNAME; + } + // Now check wether the answer packet is of the expected type + // (remember, we just tweaked CNAME answers to A queries already) + if ( (q[0] != 0) || + (q[1] != querytype) || // query type matches? + (q[2] != ((QUERYCLASS_INET & 0xff00) >> 8)) || + (q[3] != (QUERYCLASS_INET & 0x00ff))) { // class IN response? + return RET_DNSERROR; // Should not happen. DNS server bad? + } + q += 8; // skip querytype/-class/ttl which are uninteresting now + if ( querytype == QUERYTYPE_A ) { + // So what we sent was an A query - expect an IPv4 address + // Check if datalength looks satisfactory + if ( ( 0 != q[0] ) || ( 4 != q[1] ) ) { + // Data length is not 4 bytes + return RET_DNSERROR; + } + // Go to the IP address and copy it to the response buffer + p = ptr + QINDEX_STORE_A; + for ( i = 0; i < 4; ++i ) { + p[i] = q[i+2]; + } + return RET_GOT_ADDR; // OK! Address RETURNED! VERY FINE! + } else if ( querytype == QUERYTYPE_CNAME ) { // CNAME QUERY + // The pointer "q" now stands on the "payload length" of the + // CNAME data [2 byte field]. We will have to check wether this + // name is referenced in a following response, which would save + // us further queries, or if it is standalone, which calls for + // making a separate query + // This statement above probably needs clarification. To help + // the reader understand what's going on, imagine the DNS + // answer to be 4-section: query(repeating what we sent to the + // DNS server), answer(like the CNAME record we wanted), + // additional (which might hold the A for the CNAME - esp. + // Bind9 seems to provide us with this info when we didn't + // query for it yet) and authoritative (the DNS server's info + // on who is responsible for that data). For compression's + // sake, instead of specifying the full hostname string all + // the time, one can instead specify something like "same as on + // payload offset 0x123" - if this happens here, we can check + // if that payload offset given here by coincidence is that of + // an additional "A" record which saves us sending a separate + // query. + // We will look if there's a/ two or more answer sections + // AND b/ the next is a reference to us. + p = (unsigned char *)udp + sizeof(struct udphdr); + i = q + 2 - p; + if ( p[7] > 1 ) { // More than one answer section + // Check the next if it's a ref + // For that, we have to locate the next. Do this with "q". + p = q + (q[0] * 0x100) + q[1] + 2; + if ( (p[0] == (0xc0 + ((i & 0xf00)/256))) && + (p[1] == (i & 0xff) ) && + (p[2] == ((QUERYTYPE_A & 0xff00) >> 8)) && + (p[3] == (QUERYTYPE_A & 0x00ff)) && + (p[4] == ((QUERYCLASS_INET & 0xff00) >> 8 )) && + (p[5] == (QUERYCLASS_INET & 0x00ff)) && + (p[10]== 0) && // Data length expected + (p[11]== 4)) { // to be <4> (IP-addr) + // Behind that sections: TTL, data length(4) + for ( i = 0; i < 4; ++i ) { + ((unsigned char*)ptr)[QINDEX_STORE_A+i] = + p[12+i]; + } + return RET_GOT_ADDR; + } + } + // Reference not found, next query (A) needed (because CNAME + // queries usually return another hostname, not an IP address + // as we need it - that's the point in CNAME, anyway :-) + p = (unsigned char *)udp + sizeof(struct udphdr); +#ifdef DNSDEBUG + printf ( " ->["); +#endif + k = QINDEX_QUESTION; + i = (q-p) + 2; + // Compose the hostname that needs to be queried for + // This looks complicated (and is a little bit) because + // we might not be able to copy verbatim, but need to + // check wether the CNAME looks like + // "servername" "plus what offset 0x123 of the payload says" + // (this saves transfer bandwidth, which is why DNS allows + // this, but it makes programmers' lives more difficult) + while (p[i] != 0) { + if ( (((unsigned char *)p)[i] & 0xc0) != 0 ) { + i = ((p[i] & 0x3f) * 0x100) + p[i+1]; + continue; + } + ((unsigned char *)ptr)[k] = p[i]; + for ( j = i + 1; j <= i + p[i]; ++j ) { + ((unsigned char *)ptr)[k+j-i] = p[j]; +#ifdef DNSDEBUG + printf ( "%c", p[j] ); +#endif + } + k += ((unsigned char *)ptr)[k] + 1; + i += p[i] + 1; +#ifdef DNSDEBUG + printf ( (p[i] ? ".": "") ); +#endif + } + ((unsigned char *)ptr)[k] = 0; +#ifdef DNSDEBUG + printf ( "].." ); +#endif + // So we need to run another query, this time try to + // get an "A" record for the hostname that the last CNAME + // query pointed to + return RET_RUN_NEXT_A; + } + // We only accept CNAME or A replies from the nameserver, every- + // thing else is of no use for us. + // Must be an invalid packet that the nameserver sent. + return RET_DNSERROR; +} + +int chars_to_next_dot ( char * countfrom, int maxnum ) { + // Count the number of characters of this part of a hostname + int i; + for ( i = 1; i < maxnum; ++i ) { + if ( countfrom[i] == '.' ) return i; + } + return maxnum; +} + +/* + * donameresolution + * Function: Compose the initial query packet, handle answers until + * a/ an IP address is retrieved + * b/ too many CNAME references occured (max. MAX_DNS_RETRIES) + * c/ No matching record for A or CNAME can be found + * Param: string hostname, length (hostname needs no \0-end-marker), + * string to which dotted-quad-IP shall be written + * Return: 0 for success, >0 for failure + */ +int donameresolution ( char * hostname, int hnlength, char * deststring ) { + unsigned char querybuf[260+sizeof(struct iphdr)+sizeof(struct udphdr)]; + // 256 for the DNS query payload, +4 for the result temporary + unsigned char *query = &querybuf[sizeof(struct iphdr)+sizeof(struct udphdr)]; + // Pointer to the payload + int i, h = hnlength; + long timeout; + int retry, recursion; + // Setup the query data + query[QINDEX_ID ] = (QUERYIDENTIFIER & 0xff00) >> 8; + query[QINDEX_ID+1] = QUERYIDENTIFIER & 0xff; + query[QINDEX_FLAGS ] = (QUERYFLAGS & 0xff00) >> 8; + query[QINDEX_FLAGS+1] = QUERYFLAGS & 0xff; + query[QINDEX_NUMQUEST ]= 0; + query[QINDEX_NUMQUEST+1]= 1; // 1 standard query to be sent + query[QINDEX_NUMANSW ] = 0; + query[QINDEX_NUMANSW+1] = 0; + query[QINDEX_NUMAUTH ] = 0; + query[QINDEX_NUMAUTH+1] = 0; + query[QINDEX_NUMADDIT ]= 0; + query[QINDEX_NUMADDIT+1]= 0; + query[QINDEX_QUESTION] = chars_to_next_dot(hostname,h); + if ( h > 236 ) return 1; // Hostnames longer than 236 chars are refused. + // This is an arbitrary decision, SHOULD check + // what the RFCs say about that + for ( i = 0; i < h; ++i ) { + // Compose the query section's hostname - replacing dots (and + // preceding the string) with one-byte substring-length values + // (for the immediately following substring) - \0 terminated + query[QINDEX_QUESTION+i+1] = hostname[i]; + if ( hostname[i] == '.' ) + query[QINDEX_QUESTION+i+1] = chars_to_next_dot(hostname + i + 1, h - i - 1); + } + query[QINDEX_QTYPE+h ] = (QUERYTYPE_A & 0xff00) >> 8; + query[QINDEX_QTYPE+h+1] = QUERYTYPE_A & 0xff; + // First try an A query, if that + // won't work, try CNAME + printf ( "Resolving hostname [" ); + for ( i = 0; i < hnlength; ++i ) { printf ( "%c", hostname[i] ); } + printf ("]" ); + for ( recursion = MAX_CNAME_RECURSION; recursion > 0; --recursion ) { + printf ( ".." ); + // Try MAX_CNAME_RECURSION CNAME queries maximally, if then no + // A record is found, assume the hostname to not be resolvable + query[QINDEX_QUESTION+h+1] = 0; // Marks the end of + // the query string + query[QINDEX_QCLASS+h ]= (QUERYCLASS_INET & 0xff00) >> 8; + query[QINDEX_QCLASS+h+1]= QUERYCLASS_INET & 0xff; + rx_qdrain(); // Clear NIC packet buffer - + // there won't be anything of interest *now*. + udp_transmit ( arptable[ARP_NAMESERVER].ipaddr.s_addr, + UDP_PORT_DNS, UDP_PORT_DNS, + hnlength + 18 + sizeof(struct iphdr) + + sizeof(struct udphdr), querybuf ); + // If no answer comes in in a certain period of time, retry + for (retry = 1; retry <= MAX_DNS_RETRIES; retry++) { + timeout = rfc2131_sleep_interval(TIMEOUT, retry); + i = await_reply ( await_dns, + (query[QINDEX_QTYPE+h+1] << 8) + + ((unsigned char *)query)[QINDEX_ID+1], + query, timeout); + // The parameters given are + // bits 15...8 of integer = Query type (low bits) + // bits 7...0 of integer = Query index (low bits) + // query + QINDEX_STORE_A points to the place + // where A records shall be stored (4 bytes); + // query + 12(QINDEX_QUESTION) points to where + // a CNAME should go in case one is received + if (i) break; + } + switch ( i ) { + case RET_GOT_ADDR: // Address successfully retrieved + sprintf( deststring, "%@", + (long)query[QINDEX_STORE_A ] + + ( (long)query[QINDEX_STORE_A+1] * 256 ) + + ( (long)query[QINDEX_STORE_A+2] * 65536 )+ + ( (long)query[QINDEX_STORE_A+3] * 16777216 )); + printf ( " -> IP [%s]\n", deststring ); + return RET_DNS_OK; + case RET_RUN_CNAME_Q: // No A record found, try CNAME + query[QINDEX_QTYPE+h ]=(QUERYTYPE_CNAME & 0xff00)>> 8; + query[QINDEX_QTYPE+h+1]= QUERYTYPE_CNAME & 0xff; + break; + case RET_RUN_NEXT_A: + // Found a CNAME, now try A for the name it pointed to + for ( i = 0; query[QINDEX_QUESTION+i] != 0; + i += query[QINDEX_QUESTION+i] + 1 ) {;} + h = i - 1; + query[QINDEX_QTYPE+h ]=(QUERYTYPE_A & 0xff00)>> 8; + query[QINDEX_QTYPE+h+1]= QUERYTYPE_A & 0xff; + break; + case RET_CNAME_FAIL: + // Neither A nor CNAME gave a usable result + printf ("Host name cannot be resolved\n"); + return RET_DNS_FAIL; + case RET_NOSUCHNAME: + default: + printf ( "Name resolution failed\n" ); + return RET_DNS_FAIL; + } + query[QINDEX_ID ] = 0; // We will probably never have more + // than 256 queries in one run + query[QINDEX_ID+1]++; + } + // To deep recursion + printf ( "CNAME recursion to deep - abort name resolver\n" ); + return RET_DNS_FAIL; +} +#endif /* DNS_RESOLVER */ diff --git a/src/core/elf_loader.c b/src/core/elf_loader.c new file mode 100644 index 00000000..c1906d8a --- /dev/null +++ b/src/core/elf_loader.c @@ -0,0 +1,658 @@ +#include "elf.h" + +#ifndef ELF_CHECK_ARCH +#error ELF_CHECK_ARCH not defined +#endif + +#define ELF_NOTES 1 +#define ELF_DEBUG 0 + +struct elf_state +{ + union { + Elf32_Ehdr elf32; + Elf64_Ehdr elf64; + } e; + union { + Elf32_Phdr phdr32[1]; + Elf64_Phdr phdr64[1]; + unsigned char dummy[1024]; + } p; + unsigned long curaddr; + int segment; /* current segment number, -1 for none */ + uint64_t loc; /* start offset of current block */ + uint64_t skip; /* padding to be skipped to current segment */ + unsigned long toread; /* remaining data to be read in the segment */ +#if ELF_NOTES + int check_ip_checksum; + uint16_t ip_checksum; + unsigned long ip_checksum_offset; +#endif +}; + +static struct elf_state estate; + +static void elf_boot(unsigned long machine, unsigned long entry) +{ + int result; + struct Elf_Bhdr *hdr; + multiboot_boot(entry); + /* We cleanup unconditionally, and then reawaken the network + * adapter after the longjmp. + */ + hdr = prepare_boot_params(&estate.e); + result = elf_start(machine, entry, virt_to_phys(hdr)); + if (result == 0) { + result = -1; + } + printf("Secondary program returned %d\n", result); + longjmp(restart_etherboot, result); +} + +#if ELF_NOTES +static int elf_prep_segment( + unsigned long start __unused, unsigned long mid __unused, unsigned long end __unused, + unsigned long istart, unsigned long iend) + +{ + if (estate.check_ip_checksum) { + if ((istart <= estate.ip_checksum_offset) && + (iend > estate.ip_checksum_offset)) { + /* The checksum note is also loaded in a + * PT_LOAD segment, so the computed checksum + * should be 0. + */ + estate.ip_checksum = 0; + } + } + return 1; +} +#else +#define elf_prep_segment(start, mid, end, istart, iend) (1) +#endif + + +#if ELF_NOTES +static void process_elf_notes(unsigned char *header, + unsigned long offset, unsigned long length) +{ + unsigned char *note, *end; + char *program, *version; + + estate.check_ip_checksum = 0; + note = header + offset; + end = note + length; + program = version = 0; + while(note < end) { + Elf_Nhdr *hdr; + unsigned char *n_name, *n_desc, *next; + hdr = (Elf_Nhdr *)note; + n_name = note + sizeof(*hdr); + n_desc = n_name + ((hdr->n_namesz + 3) & ~3); + next = n_desc + ((hdr->n_descsz + 3) & ~3); + if (next > end) { + break; + } + if ((hdr->n_namesz == sizeof(ELF_NOTE_BOOT)) && + (memcmp(n_name, ELF_NOTE_BOOT, sizeof(ELF_NOTE_BOOT)) == 0)) { + switch(hdr->n_type) { + case EIN_PROGRAM_NAME: + if (n_desc[hdr->n_descsz -1] == 0) { + program = n_desc; + } + break; + case EIN_PROGRAM_VERSION: + if (n_desc[hdr->n_descsz -1] == 0) { + version = n_desc; + } + break; + case EIN_PROGRAM_CHECKSUM: + estate.check_ip_checksum = 1; + estate.ip_checksum = *((uint16_t *)n_desc); + /* Remember where the segment is so + * I can detect segment overlaps. + */ + estate.ip_checksum_offset = n_desc - header; +#if ELF_DEBUG + printf("Checksum: %hx\n", estate.ip_checksum); +#endif + + break; + } + } +#if ELF_DEBUG + printf("n_type: %x n_name(%d): %s n_desc(%d): %s\n", + hdr->n_type, + hdr->n_namesz, n_name, + hdr->n_descsz, n_desc); +#endif + note = next; + } + if (program && version) { + printf("\nLoading %s version: %s\n", program, version); + } +} +#endif + +#ifdef ELF_IMAGE +static sector_t elf32_download(unsigned char *data, unsigned int len, int eof); +static inline os_download_t elf32_probe(unsigned char *data, unsigned int len) +{ + unsigned long phdr_size; + if (len < sizeof(estate.e.elf32)) { + return 0; + } + memcpy(&estate.e.elf32, data, sizeof(estate.e.elf32)); + if ((estate.e.elf32.e_ident[EI_MAG0] != ELFMAG0) || + (estate.e.elf32.e_ident[EI_MAG1] != ELFMAG1) || + (estate.e.elf32.e_ident[EI_MAG2] != ELFMAG2) || + (estate.e.elf32.e_ident[EI_MAG3] != ELFMAG3) || + (estate.e.elf32.e_ident[EI_CLASS] != ELFCLASS32) || + (estate.e.elf32.e_ident[EI_DATA] != ELFDATA_CURRENT) || + (estate.e.elf32.e_ident[EI_VERSION] != EV_CURRENT) || + ( (estate.e.elf32.e_type != ET_EXEC) && + (estate.e.elf32.e_type != ET_DYN)) || + (estate.e.elf32.e_version != EV_CURRENT) || + (estate.e.elf32.e_ehsize != sizeof(Elf32_Ehdr)) || + (estate.e.elf32.e_phentsize != sizeof(Elf32_Phdr)) || + !ELF_CHECK_ARCH(estate.e.elf32)) { + return 0; + } + printf("(ELF"); + elf_freebsd_probe(); + multiboot_probe(data, len); + printf(")... "); + phdr_size = estate.e.elf32.e_phnum * estate.e.elf32.e_phentsize; + if (estate.e.elf32.e_phoff + phdr_size > len) { + printf("ELF header outside first block\n"); + return dead_download; + } + if (phdr_size > sizeof(estate.p.dummy)) { + printf("Program header to big\n"); + return dead_download; + } + memcpy(&estate.p.phdr32, data + estate.e.elf32.e_phoff, phdr_size); + if (estate.e.elf32.e_type == ET_DYN) { + Elf32_Addr min, max, base_addr, delta, align; + min = -1; + max = 0; + align = 1; + for(estate.segment = 0; estate.segment < estate.e.elf32.e_phnum; estate.segment++) { + Elf32_Addr val; + if (estate.p.phdr32[estate.segment].p_type != PT_LOAD) + continue; + val = estate.p.phdr32[estate.segment].p_paddr; + if (val < min) { + min = val; + } + val += estate.p.phdr32[estate.segment].p_memsz; + if (val > max) { + max = val; + } + if (estate.p.phdr32[estate.segment].p_align > align) { + align = estate.p.phdr32[estate.segment].p_align; + } + } + if (align & (align -1)) { + printf("ELF base address alignment is not a power of 2\n"); + return dead_download; + } + base_addr = find_segment(max - min, align); + if (base_addr == ULONG_MAX) { + printf("ELF base address not available for size %ld\n", max - min); + return dead_download; + } + /* Compute the change in base address and fix up the addresses */ + delta = base_addr - min; + for(estate.segment = 0; estate.segment < estate.e.elf32.e_phnum; estate.segment++) { + /* Change the base address of the object to load */ + estate.p.phdr32[estate.segment].p_paddr += delta; + } + estate.e.elf32.e_entry += delta; + } +#if ELF_NOTES + /* Load ELF notes from the image */ + for(estate.segment = 0; estate.segment < estate.e.elf32.e_phnum; estate.segment++) { + if (estate.p.phdr32[estate.segment].p_type != PT_NOTE) + continue; + if (estate.p.phdr32[estate.segment].p_offset + estate.p.phdr32[estate.segment].p_filesz > len) { + /* Ignore ELF notes outside of the first block */ + continue; + } + process_elf_notes(data, + estate.p.phdr32[estate.segment].p_offset, estate.p.phdr32[estate.segment].p_filesz); + } +#endif + /* Check for Etherboot related limitations. Memory + * between _text and _end is not allowed. + * Reasons: the Etherboot code/data area. + */ + for (estate.segment = 0; estate.segment < estate.e.elf32.e_phnum; estate.segment++) { + unsigned long start, mid, end, istart, iend; + if (estate.p.phdr32[estate.segment].p_type != PT_LOAD) + continue; + + elf_freebsd_fixup_segment(); + + start = estate.p.phdr32[estate.segment].p_paddr; + mid = start + estate.p.phdr32[estate.segment].p_filesz; + end = start + estate.p.phdr32[estate.segment].p_memsz; + istart = estate.p.phdr32[estate.segment].p_offset; + iend = istart + estate.p.phdr32[estate.segment].p_filesz; + if (!prep_segment(start, mid, end, istart, iend)) { + return dead_download; + } + if (!elf_prep_segment(start, mid, end, istart, iend)) { + return dead_download; + } + } + estate.segment = -1; + estate.loc = 0; + estate.skip = 0; + estate.toread = 0; + return elf32_download; +} + +static sector_t elf32_download(unsigned char *data, unsigned int len, int eof) +{ + unsigned long skip_sectors = 0; + unsigned int offset; /* working offset in the current data block */ + int i; + + offset = 0; + do { + if (estate.segment != -1) { + if (estate.skip) { + if (estate.skip >= len - offset) { + estate.skip -= len - offset; + break; + } + offset += estate.skip; + estate.skip = 0; + } + + if (estate.toread) { + unsigned int cplen; + cplen = len - offset; + if (cplen >= estate.toread) { + cplen = estate.toread; + } + memcpy(phys_to_virt(estate.curaddr), data+offset, cplen); + estate.curaddr += cplen; + estate.toread -= cplen; + offset += cplen; + if (estate.toread) + break; + elf_freebsd_find_segment_end(); + } + } + + /* Data left, but current segment finished - look for the next + * segment (in file offset order) that needs to be loaded. + * We can only seek forward, so select the program headers, + * in the correct order. + */ + estate.segment = -1; + for (i = 0; i < estate.e.elf32.e_phnum; i++) { + if (estate.p.phdr32[i].p_type != PT_LOAD) + continue; + if (estate.p.phdr32[i].p_filesz == 0) + continue; + if (estate.p.phdr32[i].p_offset < estate.loc + offset) + continue; /* can't go backwards */ + if ((estate.segment != -1) && + (estate.p.phdr32[i].p_offset >= estate.p.phdr32[estate.segment].p_offset)) + continue; /* search minimum file offset */ + estate.segment = i; + } + if (estate.segment == -1) { + if (elf_freebsd_debug_loader(offset)) { + estate.segment = 0; /* -1 makes it not read anymore */ + continue; + } + /* No more segments to be loaded, so just start the + * kernel. This saves a lot of network bandwidth if + * debug info is in the kernel but not loaded. */ + goto elf_startkernel; + break; + } + estate.curaddr = estate.p.phdr32[estate.segment].p_paddr; + estate.skip = estate.p.phdr32[estate.segment].p_offset - (estate.loc + offset); + estate.toread = estate.p.phdr32[estate.segment].p_filesz; +#if ELF_DEBUG + printf("PHDR %d, size %#lX, curaddr %#lX\n", + estate.segment, estate.toread, estate.curaddr); +#endif + } while (offset < len); + + estate.loc += len + (estate.skip & ~0x1ff); + skip_sectors = estate.skip >> 9; + estate.skip &= 0x1ff; + + if (eof) { + unsigned long entry; + unsigned long machine; +elf_startkernel: + entry = estate.e.elf32.e_entry; + machine = estate.e.elf32.e_machine; + +#if ELF_NOTES + if (estate.check_ip_checksum) { + unsigned long bytes = 0; + uint16_t sum, new_sum; + + sum = ipchksum(&estate.e.elf32, sizeof(estate.e.elf32)); + bytes = sizeof(estate.e.elf32); +#if ELF_DEBUG + printf("Ehdr: %hx %hx sz: %lx bytes: %lx\n", + sum, sum, bytes, bytes); +#endif + + new_sum = ipchksum(estate.p.phdr32, sizeof(estate.p.phdr32[0]) * estate.e.elf32.e_phnum); + sum = add_ipchksums(bytes, sum, new_sum); + bytes += sizeof(estate.p.phdr32[0]) * estate.e.elf32.e_phnum; +#if ELF_DEBUG + printf("Phdr: %hx %hx sz: %lx bytes: %lx\n", + new_sum, sum, + sizeof(estate.p.phdr32[0]) * estate.e.elf32.e_phnum, bytes); +#endif + + for(i = 0; i < estate.e.elf32.e_phnum; i++) { + if (estate.p.phdr32[i].p_type != PT_LOAD) + continue; + new_sum = ipchksum(phys_to_virt(estate.p.phdr32[i].p_paddr), + estate.p.phdr32[i].p_memsz); + sum = add_ipchksums(bytes, sum, new_sum); + bytes += estate.p.phdr32[i].p_memsz; +#if ELF_DEBUG + printf("seg%d: %hx %hx sz: %x bytes: %lx\n", + i, new_sum, sum, + estate.p.phdr32[i].p_memsz, bytes); +#endif + + } + if (estate.ip_checksum != sum) { + printf("\nImage checksum: %hx != computed checksum: %hx\n", + estate.ip_checksum, sum); + longjmp(restart_etherboot, -2); + } + } +#endif + done(1); + /* Fixup the offset to the program header so you can find the program headers from + * the ELF header mknbi needs this. + */ + estate.e.elf32.e_phoff = (char *)&estate.p - (char *)&estate.e; + elf_freebsd_boot(entry); + elf_boot(machine,entry); + } + return skip_sectors; +} +#endif /* ELF_IMAGE */ + +#ifdef ELF64_IMAGE +static sector_t elf64_download(unsigned char *data, unsigned int len, int eof); +static inline os_download_t elf64_probe(unsigned char *data, unsigned int len) +{ + unsigned long phdr_size; + if (len < sizeof(estate.e.elf64)) { + return 0; + } + memcpy(&estate.e.elf64, data, sizeof(estate.e.elf64)); + if ((estate.e.elf64.e_ident[EI_MAG0] != ELFMAG0) || + (estate.e.elf64.e_ident[EI_MAG1] != ELFMAG1) || + (estate.e.elf64.e_ident[EI_MAG2] != ELFMAG2) || + (estate.e.elf64.e_ident[EI_MAG3] != ELFMAG3) || + (estate.e.elf64.e_ident[EI_CLASS] != ELFCLASS64) || + (estate.e.elf64.e_ident[EI_DATA] != ELFDATA_CURRENT) || + (estate.e.elf64.e_ident[EI_VERSION] != EV_CURRENT) || + ( (estate.e.elf64.e_type != ET_EXEC) && + (estate.e.elf64.e_type != ET_DYN)) || + (estate.e.elf64.e_version != EV_CURRENT) || + (estate.e.elf64.e_ehsize != sizeof(Elf64_Ehdr)) || + (estate.e.elf64.e_phentsize != sizeof(Elf64_Phdr)) || + !ELF_CHECK_ARCH(estate.e.elf64)) { + return 0; + } + printf("(ELF64)... "); + phdr_size = estate.e.elf64.e_phnum * estate.e.elf64.e_phentsize; + if (estate.e.elf64.e_phoff + phdr_size > len) { + printf("ELF header outside first block\n"); + return dead_download; + } + if (phdr_size > sizeof(estate.p.dummy)) { + printf("Program header to big\n"); + return dead_download; + } + if (estate.e.elf64.e_entry > ULONG_MAX) { + printf("ELF entry point exceeds address space\n"); + return dead_download; + } + memcpy(&estate.p.phdr64, data + estate.e.elf64.e_phoff, phdr_size); + if (estate.e.elf64.e_type == ET_DYN) { + Elf64_Addr min, max, base_addr, delta, align; + min = -1; + max = 0; + align = 1; + for(estate.segment = 0; estate.segment < estate.e.elf64.e_phnum; estate.segment++) { + Elf64_Addr val; + if (estate.p.phdr64[estate.segment].p_type != PT_LOAD) + continue; + val = estate.p.phdr64[estate.segment].p_paddr; + if (val < min) { + min = val; + } + val += estate.p.phdr64[estate.segment].p_memsz; + if (val > max) { + max = val; + } + if (estate.p.phdr64[estate.segment].p_align > align) { + align = estate.p.phdr64[estate.segment].p_align; + } + } + if (align > ULONG_MAX) { + printf("ELF base address alignment exceeds address space\n"); + return dead_download; + } + if (align & (align -1)) { + printf("ELF base address alignment is not a power of 2\n"); + return dead_download; + } + if ((max - min) > ULONG_MAX) { + printf("ELF size exceeds address space\n"); + return dead_download; + } + base_addr = find_segment(max - min, align); + if (base_addr == ULONG_MAX) { + printf("ELF base address not available for size %ld\n", max - min); + return dead_download; + } + /* Compute the change in base address and fix up the addresses */ + delta = base_addr - min; + for(estate.segment = 0; estate.segment < estate.e.elf64.e_phnum; estate.segment++) { + /* Change the base address of the object to load */ + estate.p.phdr64[estate.segment].p_paddr += delta; + } + estate.e.elf64.e_entry += delta; + } +#if ELF_NOTES + /* Load ELF notes from the image */ + for(estate.segment = 0; estate.segment < estate.e.elf64.e_phnum; estate.segment++) { + if (estate.p.phdr64[estate.segment].p_type != PT_NOTE) + continue; + if (estate.p.phdr64[estate.segment].p_offset + estate.p.phdr64[estate.segment].p_filesz > len) { + /* Ignore ELF notes outside of the first block */ + continue; + } + process_elf_notes(data, + estate.p.phdr64[estate.segment].p_offset, estate.p.phdr64[estate.segment].p_filesz); + } +#endif + /* Check for Etherboot related limitations. Memory + * between _text and _end is not allowed. + * Reasons: the Etherboot code/data area. + */ + for (estate.segment = 0; estate.segment < estate.e.elf64.e_phnum; estate.segment++) { + unsigned long start, mid, end, istart, iend; + if (estate.p.phdr64[estate.segment].p_type != PT_LOAD) + continue; + if ((estate.p.phdr64[estate.segment].p_paddr > ULONG_MAX) || + ((estate.p.phdr64[estate.segment].p_paddr + estate.p.phdr64[estate.segment].p_filesz) > ULONG_MAX) || + ((estate.p.phdr64[estate.segment].p_paddr + estate.p.phdr64[estate.segment].p_memsz) > ULONG_MAX)) { + printf("ELF segment exceeds address space\n"); + return dead_download; + } + start = estate.p.phdr64[estate.segment].p_paddr; + mid = start + estate.p.phdr64[estate.segment].p_filesz; + end = start + estate.p.phdr64[estate.segment].p_memsz; + istart = iend = ULONG_MAX; + if ((estate.p.phdr64[estate.segment].p_offset < ULONG_MAX) && + ((estate.p.phdr64[estate.segment].p_offset + estate.p.phdr64[estate.segment].p_filesz) < ULONG_MAX)) + { + istart = estate.p.phdr64[estate.segment].p_offset; + iend = istart + estate.p.phdr64[estate.segment].p_filesz; + } + if (!prep_segment(start, mid, end, istart, iend)) { + return dead_download; + } + if (!elf_prep_segment(start, mid, end, istart, iend)) { + return dead_download; + } + } + estate.segment = -1; + estate.loc = 0; + estate.skip = 0; + estate.toread = 0; + return elf64_download; +} + +static sector_t elf64_download(unsigned char *data, unsigned int len, int eof) +{ + unsigned long skip_sectors = 0; + unsigned int offset; /* working offset in the current data block */ + int i; + + offset = 0; + do { + if (estate.segment != -1) { + if (estate.skip) { + if (estate.skip >= len - offset) { + estate.skip -= len - offset; + break; + } + offset += estate.skip; + estate.skip = 0; + } + + if (estate.toread) { + unsigned int cplen; + cplen = len - offset; + if (cplen >= estate.toread) { + cplen = estate.toread; + } + memcpy(phys_to_virt(estate.curaddr), data+offset, cplen); + estate.curaddr += cplen; + estate.toread -= cplen; + offset += cplen; + if (estate.toread) + break; + } + } + + /* Data left, but current segment finished - look for the next + * segment (in file offset order) that needs to be loaded. + * We can only seek forward, so select the program headers, + * in the correct order. + */ + estate.segment = -1; + for (i = 0; i < estate.e.elf64.e_phnum; i++) { + if (estate.p.phdr64[i].p_type != PT_LOAD) + continue; + if (estate.p.phdr64[i].p_filesz == 0) + continue; + if (estate.p.phdr64[i].p_offset < estate.loc + offset) + continue; /* can't go backwards */ + if ((estate.segment != -1) && + (estate.p.phdr64[i].p_offset >= estate.p.phdr64[estate.segment].p_offset)) + continue; /* search minimum file offset */ + estate.segment = i; + } + if (estate.segment == -1) { + /* No more segments to be loaded, so just start the + * kernel. This saves a lot of network bandwidth if + * debug info is in the kernel but not loaded. */ + goto elf_startkernel; + break; + } + estate.curaddr = estate.p.phdr64[estate.segment].p_paddr; + estate.skip = estate.p.phdr64[estate.segment].p_offset - (estate.loc + offset); + estate.toread = estate.p.phdr64[estate.segment].p_filesz; +#if ELF_DEBUG + printf("PHDR %d, size %#lX, curaddr %#lX\n", + estate.segment, estate.toread, estate.curaddr); +#endif + } while (offset < len); + + estate.loc += len + (estate.skip & ~0x1ff); + skip_sectors = estate.skip >> 9; + estate.skip &= 0x1ff; + + if (eof) { + unsigned long entry; + unsigned long machine; +elf_startkernel: + entry = estate.e.elf64.e_entry; + machine = estate.e.elf64.e_machine; +#if ELF_NOTES + if (estate.check_ip_checksum) { + unsigned long bytes = 0; + uint16_t sum, new_sum; + + sum = ipchksum(&estate.e.elf64, sizeof(estate.e.elf64)); + bytes = sizeof(estate.e.elf64); +#if ELF_DEBUG + printf("Ehdr: %hx %hx sz: %lx bytes: %lx\n", + sum, sum, bytes, bytes); +#endif + + new_sum = ipchksum(estate.p.phdr64, sizeof(estate.p.phdr64[0]) * estate.e.elf64.e_phnum); + sum = add_ipchksums(bytes, sum, new_sum); + bytes += sizeof(estate.p.phdr64[0]) * estate.e.elf64.e_phnum; +#if ELF_DEBUG + printf("Phdr: %hx %hx sz: %lx bytes: %lx\n", + new_sum, sum, + sizeof(estate.p.phdr64[0]) * estate.e.elf64.e_phnum, bytes); +#endif + + for(i = 0; i < estate.e.elf64.e_phnum; i++) { + if (estate.p.phdr64[i].p_type != PT_LOAD) + continue; + new_sum = ipchksum(phys_to_virt(estate.p.phdr64[i].p_paddr), + estate.p.phdr64[i].p_memsz); + sum = add_ipchksums(bytes, sum, new_sum); + bytes += estate.p.phdr64[i].p_memsz; +#if ELF_DEBUG + printf("seg%d: %hx %hx sz: %x bytes: %lx\n", + i, new_sum, sum, + estate.p.phdr64[i].p_memsz, bytes); +#endif + + } + if (estate.ip_checksum != sum) { + printf("\nImage checksum: %hx != computed checksum: %hx\n", + estate.ip_checksum, sum); + longjmp(restart_etherboot, -2); + } + } +#endif + done(1); + /* Fixup the offset to the program header so you can find the program headers from + * the ELF header mknbi needs this. + */ + estate.e.elf64.e_phoff = (char *)&estate.p - (char *)&estate.e; + elf_boot(machine,entry); + } + return skip_sectors; +} + +#endif /* ELF64_IMAGE */ diff --git a/src/core/heap.c b/src/core/heap.c new file mode 100644 index 00000000..51ce47d7 --- /dev/null +++ b/src/core/heap.c @@ -0,0 +1,168 @@ +#include "etherboot.h" + +size_t heap_ptr, heap_top, heap_bot; + +void init_heap(void) +{ + size_t size; + size_t start, end; + unsigned i; + /* Find the largest contiguous area of memory that + * I can use for the heap, which is organized as + * a stack that grows backwards through memory. + */ + + /* If I have virtual address that do not equal physical addresses + * there is a change I will try to use memory from both sides of + * the virtual address space simultaneously, which can cause all kinds + * of interesting problems. + * Avoid it by logically extending etherboot. Once I know that relocation + * works I can just start the virtual address space at 0, and this problem goes + * away so that is probably a better solution. + */ +#if 0 + start = virt_to_phys(_text); +#else + /* segment wrap around is nasty don't chance it. */ + start = virt_to_phys(_virt_start); +#endif + end = virt_to_phys(_end); + size = 0; + for(i = 0; i < meminfo.map_count; i++) { + unsigned long r_start, r_end; + if (meminfo.map[i].type != E820_RAM) + continue; + if (meminfo.map[i].addr > ULONG_MAX) + continue; + if (meminfo.map[i].size > ULONG_MAX) + continue; + + r_start = meminfo.map[i].addr; + r_end = r_start + meminfo.map[i].size; + if (r_end < r_start) { + r_end = ULONG_MAX; + } + /* Handle areas that overlap etherboot */ + if ((end > r_start) && (start < r_end)) { + /* Etherboot completely covers the region */ + if ((start <= r_start) && (end >= r_end)) + continue; + /* Etherboot is completely contained in the region */ + if ((start > r_start) && (end < r_end)) { + /* keep the larger piece */ + if ((r_end - end) >= (r_start - start)) { + r_start = end; + } + else { + r_end = start; + } + } + /* Etherboot covers one end of the region. + * Shrink the region. + */ + else if (end >= r_end) { + r_end = start; + } + else if (start <= r_start) { + r_start = end; + } + } + /* If two areas are the size prefer the greater address */ + if (((r_end - r_start) > size) || + (((r_end - r_start) == size) && (r_start > heap_top))) { + size = r_end - r_start; + heap_top = r_start; + heap_bot = r_end; + } + } + if (size == 0) { + printf("init_heap: No heap found.\n"); + exit(1); + } + heap_ptr = heap_bot; +} + +void *allot(size_t size) +{ + void *ptr; + size_t *mark, addr; + /* Get an 16 byte aligned chunk of memory off of the heap + * An extra sizeof(size_t) bytes is allocated to track + * the size of the object allocated on the heap. + */ + addr = (heap_ptr - (size + sizeof(size_t))) & ~15; + if (addr < heap_top) { + ptr = 0; + } else { + mark = phys_to_virt(addr); + *mark = size; + heap_ptr = addr; + ptr = phys_to_virt(addr + sizeof(size_t)); + } + return ptr; +} + +//if mask = 0xf, it will be 16 byte aligned +//if mask = 0xff, it will be 256 byte aligned +//For DMA memory allocation, because it has more reqiurement on alignment +void *allot2(size_t size, uint32_t mask) +{ + void *ptr; + size_t *mark, addr; + uint32_t *mark1; + + addr = ((heap_ptr - size ) & ~mask) - sizeof(size_t) - sizeof(uint32_t); + if (addr < heap_top) { + ptr = 0; + } else { + mark = phys_to_virt(addr); + *mark = size; + mark1 = phys_to_virt(addr+sizeof(size_t)); + *mark1 = mask; + heap_ptr = addr; + ptr = phys_to_virt(addr + sizeof(size_t) + sizeof(uint32_t)); + } + return ptr; +} + +void forget(void *ptr) +{ + size_t *mark, addr; + size_t size; + + if (!ptr) { + return; + } + addr = virt_to_phys(ptr); + mark = phys_to_virt(addr - sizeof(size_t)); + size = *mark; + addr += (size + 15) & ~15; + + if (addr > heap_bot) { + addr = heap_bot; + } + heap_ptr = addr; +} + +void forget2(void *ptr) +{ + size_t *mark, addr; + size_t size; + uint32_t mask; + uint32_t *mark1; + + if (!ptr) { + return; + } + addr = virt_to_phys(ptr); + mark = phys_to_virt(addr - sizeof(size_t) - sizeof(uint32_t)); + size = *mark; + mark1 = phys_to_virt(addr - sizeof(uint32_t)); + mask = *mark1; + addr += (size + mask) & ~mask; + + if (addr > heap_bot) { + addr = heap_bot; + } + heap_ptr = addr; +} diff --git a/src/core/i82365.c b/src/core/i82365.c new file mode 100644 index 00000000..c26639e0 --- /dev/null +++ b/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 + * . 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/src/core/isa_probe.c b/src/core/isa_probe.c new file mode 100644 index 00000000..2bc523e7 --- /dev/null +++ b/src/core/isa_probe.c @@ -0,0 +1,66 @@ +#ifdef CONFIG_ISA +/* + * 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 "isa.h" + +void isa_enumerate(void) +{ + const struct isa_driver *driver; + for(driver = isa_drivers; driver < isa_drivers_end; driver++) { + printf("%s ", driver->name); + } + +} + +int isa_probe(struct dev *dev, const char *type_name) +{ +/* + * NIC probing is in the order the drivers were linked togeter. + * If for some reason you want to change the order, + * just change the order you list the drivers in. + */ + struct isa_probe_state *state = &dev->state.isa; + printf("Probing isa %s...\n", type_name); + if (dev->how_probe == PROBE_FIRST) { + state->advance = 0; + state->driver = isa_drivers; + dev->index = -1; + } + for(;;) + { + if ((dev->how_probe != PROBE_AWAKE) && state->advance) { + state->driver++; + dev->index = -1; + } + state->advance = 1; + + if (state->driver >= isa_drivers_end) + break; + + if (state->driver->type != dev->type) + continue; + + if (dev->how_probe != PROBE_AWAKE) { + dev->type_index++; + } + printf("[%s]", state->driver->name); + dev->devid.bus_type = ISA_BUS_TYPE; + /* FIXME how do I handle dev->index + PROBE_AGAIN?? */ + /* driver will fill in vendor and device IDs */ + if (state->driver->probe(dev, state->driver->ioaddrs)) { + state->advance = (dev->index == -1); + return PROBE_WORKED; + } + putchar('\n'); + } + return PROBE_FAILED; +} + +#endif diff --git a/src/core/isapnp.c b/src/core/isapnp.c new file mode 100644 index 00000000..b03da92c --- /dev/null +++ b/src/core/isapnp.c @@ -0,0 +1,382 @@ +#ifdef CONFIG_ISA +/************************************************************************** +* +* isapnp.c -- Etherboot isapnp support for the 3Com 3c515 +* Written 2002-2003 by Timothy Legge +* +* 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 +* +* +* Indent Options: indent -kr -i8 +***************************************************************************/ + +/* to get some global routines like printf */ +#include "etherboot.h" +#include "timer.h" +#include "isapnp.h" + +static int pnp_card_csn = 0; + +void isapnp_wait(unsigned int nticks) +{ + unsigned int to = currticks() + nticks; + while (currticks() < to) + /* Wait */ ; +} + +/* The following code is the ISA PNP logic required to activate the 3c515 */ +/* PNP Defines */ +#define IDENT_LEN 9 +#define NUM_CARDS 128 + +/* PNP declares */ +static unsigned char serial_identifier[NUM_CARDS + 1][IDENT_LEN]; +static unsigned char isapnp_checksum_value; +static char initdata[INIT_LENGTH] = INITDATA; +int read_port = 0; + + +/* PNP Prototypes */ +static int Isolate(void); +static int do_isapnp_isolate(void); +static int isapnp_build_device_list(void); +static int isapnp_isolate_rdp_select(void); +static int isapnp_next_rdp(void); +static void isapnp_peek(unsigned char *data, int bytes); +static void send_key(void); +static unsigned char isapnp_checksum(unsigned char *data); +int Config(int csn); + +void config_pnp_device(void) +{ + /* PNP Configuration */ + printf("Probing/Configuring ISAPNP devices\n"); + if (!read_port) { + Isolate(); + if (pnp_card_csn) + Config(pnp_card_csn); + } +} + +/* Isolate all the PNP Boards on the ISA BUS */ +static int Isolate(void) +{ + int cards = 0; + if (read_port < 0x203 || read_port > 0x3ff) { + cards = do_isapnp_isolate(); + if (cards < 0 || (read_port < 0x203 || read_port > 0x3ff)) { + printf("No Plug & Play device found\n"); + return 0; + } + } + isapnp_build_device_list(); +#ifdef EDEBUG + printf("%d Plug & Play device found\n", cards); +#endif + return 0; +} + +static int do_isapnp_isolate(void) +{ + unsigned char checksum = 0x6a; + unsigned char chksum = 0x00; + unsigned char bit = 0x00; + unsigned char c1, c2; + int csn = 0; + int i; + int iteration = 1; + + read_port = 0x213; + if (isapnp_isolate_rdp_select() < 0) + return -1; + + while (1) { + for (i = 1; i <= 64; i++) { + c1 = READ_DATA; + isapnp_wait(1); + c2 = READ_DATA; + isapnp_wait(1); + if (c1 == 0x55) { + if (c2 == 0xAA) { + bit = 0x01; + } + } + checksum = + ((((checksum ^ (checksum >> 1)) & 0x01) ^ bit) + << 7) | (checksum >> 1); + bit = 0x00; + } +#ifdef EDEBUG + printf("Calc checksum %d", checksum); +#endif + for (i = 65; i <= 72; i++) { + c1 = READ_DATA; + udelay(250); + c2 = READ_DATA; + udelay(250); + if (c1 == 0x55) { + if (c2 == 0xAA) + chksum |= (1 << (i - 65)); + } + } +#ifdef EDEBUG + printf("Actual checksum %d", chksum); +#endif + if (checksum != 0x00 && checksum == chksum) { + csn++; + serial_identifier[csn][iteration] >>= 1; + serial_identifier[csn][iteration] |= bit; + CARDSELECTNUMBER; +#ifdef EDEBUG + printf("Writing csn: %d", csn); +#endif + WRITE_DATA(csn); + udelay(250); + iteration++; + /* Force all cards without a CSN into Isolation state */ + Wake(0); + SetRdPort(read_port); + udelay(1000); + SERIALISOLATION; + udelay(1000); + goto __next; + } + if (iteration == 1) { + read_port += READ_ADDR_STEP; + if (isapnp_isolate_rdp_select() < 0) + return -1; + } else if (iteration > 1) { + break; + } + __next: + checksum = 0x6a; + chksum = 0x00; + bit = 0x00; + } + return csn; +} + +/* + * Build device list for all present ISA PnP devices. + */ +static int isapnp_build_device_list(void) +{ + int csn, device, vendor, serial; + unsigned char header[9], checksum; + for (csn = 1; csn <= 10; csn++) { + Wake(csn); + isapnp_peek(header, 9); + checksum = isapnp_checksum(header); +#ifdef EDEBUG + printf + ("vendor: 0x%hX:0x%hX:0x%hX:0x%hX:0x%hX:0x%hX:0x%hX:0x%hX:0x%hX\n", + header[0], header[1], header[2], header[3], header[4], + header[5], header[6], header[7], header[8]); + printf("checksum = 0xhX\n", checksum); +#endif + /* Don't be strict on the checksum, here ! + e.g. 'SCM SwapBox Plug and Play' has header[8]==0 (should be: b7) */ + if (header[8] == 0); + else if (checksum == 0x00 || checksum != header[8]) /* not valid CSN */ + continue; + + vendor = (header[1] << 8) | header[0]; + device = (header[3] << 8) | header[2]; + serial = + (header[7] << 24) | (header[6] << 16) | (header[5] << + 8) | + header[4]; + if (vendor == 0x6D50) + if (device == 0x5150) { + printf + ("\nFound 3Com 3c515 PNP Card!\n Vendor ID: 0x%hX, Device ID: 0x%hX, Serial Num: 0x%hX\n", + vendor, device, serial); + pnp_card_csn = csn; + } + isapnp_checksum_value = 0x00; + } + return 0; +} + +int Config(int csn) +{ +#define TIMEOUT_PNP 100 + unsigned char id[IDENT_LEN]; + int i, x; + Wake(csn); + udelay(1000); + for (i = 0; i < IDENT_LEN; i++) { + for (x = 1; x < TIMEOUT_PNP; x++) { + if (STATUS & 1) + break; + udelay(1000); + } + id[i] = RESOURCEDATA; +#ifdef EDEBUG + printf(" 0x%hX ", id[i]); +#endif + } +#ifdef EDEBUG + printf("Got The status bit\n"); +#endif + /*Set Logical Device Register active */ + LOGICALDEVICENUMBER; + /* Specify the first logical device */ + WRITE_DATA(0); + + + /* Apparently just activating the card is enough + for Etherboot to detect it. Why bother with the + following code. Left in place in case it is + later required */ +/*==========================================*/ + /* set DMA */ +/* ADDRESS(0x74 + 0); + WRITE_DATA(7); */ + + /*Set IRQ */ +/* udelay(1000); + ADDRESS(0x70 + (0 << 1)); + WRITE_DATA(9); + udelay(1000); */ +/*=============================================*/ + /*Activate */ + ACTIVATE; + WRITE_DATA(1); + udelay(250); + /* Ask for access to the Wait for Key command - ConfigControl register */ + CONFIGCONTROL; + /* Write the Wait for Key Command to the ConfigControl Register */ + WRITE_DATA(CONFIG_WAIT_FOR_KEY); + /* As per doc. Two Write cycles of 0x00 required befor the Initialization key is sent */ + ADDRESS(0); + ADDRESS(0); + + return 1; +} + +static void send_key(void) +{ + int i; + /* Ask for access to the Wait for Key command - ConfigControl register */ + CONFIGCONTROL; + /* Write the Wait for Key Command to the ConfigControl Register */ + WRITE_DATA(CONFIG_WAIT_FOR_KEY); + /* As per doc. Two Write cycles of 0x00 required befor the Initialization key is sent */ + ADDRESS(0); + ADDRESS(0); + /* 32 writes of the initiation key to the card */ + for (i = 0; i < INIT_LENGTH; i++) + ADDRESS(initdata[i]); +} + +static void isapnp_peek(unsigned char *data, int bytes) +{ + int i, j; + unsigned char d = 0; + + for (i = 1; i <= bytes; i++) { + for (j = 0; j < 20; j++) { + d = STATUS; + if (d & 1) + break; + udelay(100); + } + if (!(d & 1)) { + if (data != NULL) + *data++ = 0xff; + continue; + } + d = RESOURCEDATA; /* PRESDI */ + isapnp_checksum_value += d; + if (data != NULL) + *data++ = d; + } +} + +/* + * Compute ISA PnP checksum for first eight bytes. + */ +static unsigned char isapnp_checksum(unsigned char *data) +{ + int i, j; + unsigned char checksum = 0x6a, bit, b; + + for (i = 0; i < 8; i++) { + b = data[i]; + for (j = 0; j < 8; j++) { + bit = 0; + if (b & (1 << j)) + bit = 1; + checksum = + ((((checksum ^ (checksum >> 1)) & 0x01) ^ bit) + << 7) | (checksum >> 1); + } + } + return checksum; +} +static int isapnp_next_rdp(void) +{ + int rdp = read_port; + while (rdp <= 0x3ff) { + /* + * We cannot use NE2000 probe spaces for ISAPnP or we + * will lock up machines. + */ + if ((rdp < 0x280 || rdp > 0x380)) { + read_port = rdp; + return 0; + } + rdp += READ_ADDR_STEP; + } + return -1; +} + +static int isapnp_isolate_rdp_select(void) +{ + send_key(); + /* Control: reset CSN and conditionally everything else too */ + CONFIGCONTROL; + WRITE_DATA((CONFIG_RESET_CSN | CONFIG_WAIT_FOR_KEY)); + mdelay(2); + + send_key(); + Wake(0); + + if (isapnp_next_rdp() < 0) { + /* Ask for access to the Wait for Key command - ConfigControl register */ + CONFIGCONTROL; + /* Write the Wait for Key Command to the ConfigControl Register */ + WRITE_DATA(CONFIG_WAIT_FOR_KEY); + return -1; + } + + SetRdPort(read_port); + udelay(1000); + SERIALISOLATION; + udelay(1000); + return 0; +} +#endif /* CONFIG_ISA */ diff --git a/src/core/main.c b/src/core/main.c new file mode 100644 index 00000000..e92d2929 --- /dev/null +++ b/src/core/main.c @@ -0,0 +1,529 @@ +/************************************************************************** +Etherboot - 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 + +**************************************************************************/ + +/* #define MDEBUG */ + +#include "etherboot.h" +#include "dev.h" +#include "nic.h" +#include "disk.h" +#include "http.h" +#include "timer.h" +#include "cpu.h" +#include + +#ifdef CONSOLE_BTEXT +#include "btext.h" +#endif + +#ifdef CONFIG_FILO +#include +#endif + +jmp_buf restart_etherboot; +int url_port; + +char as_main_program = 1; + +#ifdef IMAGE_FREEBSD +int freebsd_howto = 0; +char freebsd_kernel_env[FREEBSD_KERNEL_ENV_SIZE]; +#endif + +/* in_call(): the entry point to Etherboot. Generally called from + * arch_in_call(), which in turn will have been invoked from + * platform-specific assembly code. + */ +int in_call ( in_call_data_t *data, uint32_t opcode, va_list params ) { + int old_as_main_program = as_main_program; + int ret = 0; + + /* Set flat to indicate that we are not running as the main + * program (i.e. we are something like a PXE stack). + */ + as_main_program = 0; + + /* NOTE: params will cease to be valid if we relocate, since + * it represents a virtual address + */ + switch ( EB_OPCODE(opcode) ) { + + case EB_OPCODE_CHECK: + /* Installation check + */ + ret = EB_CHECK_RESULT; + break; + case EB_OPCODE_MAIN: + /* Start up Etherboot as a standalone program. */ + as_main_program = 1; + ret = main ( data, params ); + break; +#ifdef PXE_EXPORT + case EB_OPCODE_PXE: + /* !PXE API call */ + ret = pxe_in_call ( data, params ); + break; +#endif + default: + printf ( "Unsupported API \"%c%c\"\n", + EB_OPCODE(opcode) >> 8, EB_OPCODE(opcode) & 0xff ); + ret = -1; + break; + } + + as_main_program = old_as_main_program; + return ret; +} + +static inline unsigned long ask_boot(unsigned *index) +{ + unsigned long order = DEFAULT_BOOT_ORDER; + *index = DEFAULT_BOOT_INDEX; +#ifdef LINUXBIOS + order = get_boot_order(order, index); +#endif +#if defined(ASK_BOOT) +#if ASK_BOOT >= 0 + while(1) { + int c = 0; + printf(ASK_PROMPT); +#if ASK_BOOT > 0 + { + unsigned long time; + for ( time = currticks() + ASK_BOOT*TICKS_PER_SEC; + !c && !iskey(); ) { + if (currticks() > time) c = ANS_DEFAULT; + } + } +#endif /* ASK_BOOT > 0 */ + if ( !c ) c = getchar(); + if ((c >= 'a') && (c <= 'z')) c &= 0x5F; + if ((c >= ' ') && (c <= '~')) putchar(c); + putchar('\n'); + + switch(c) { + default: + /* Nothing useful try again */ + continue; + case ANS_QUIT: + order = BOOT_NOTHING; + *index = 0; + break; + case ANS_DEFAULT: + /* Preserve the default boot order */ + break; + case ANS_NETWORK: + order = (BOOT_NIC << (0*BOOT_BITS)) | + (BOOT_NOTHING << (1*BOOT_BITS)); + *index = 0; + break; + case ANS_DISK: + order = (BOOT_DISK << (0*BOOT_BITS)) | + (BOOT_NOTHING << (1*BOOT_BITS)); + *index = 0; + break; + case ANS_FLOPPY: + order = (BOOT_FLOPPY << (0*BOOT_BITS)) | + (BOOT_NOTHING << (1*BOOT_BITS)); + *index = 0; + break; + } + break; + } + putchar('\n'); +#endif /* ASK_BOOT >= 0 */ +#endif /* defined(ASK_BOOT) */ + return order; +} + +static inline void try_floppy_first(void) +{ +#if (TRY_FLOPPY_FIRST > 0) + int i; + printf("Trying floppy"); + disk_init(); + for (i = TRY_FLOPPY_FIRST; i-- > 0; ) { + putchar('.'); + if (pcbios_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 */ +} + +void console_init(void) +{ +#ifdef CONSOLE_SERIAL + (void)serial_init(); +#endif +#ifdef CONSOLE_DIRECT_VGA + video_init(); +#endif +#ifdef CONSOLE_BTEXT + map_boot_text(); +#endif +} + +static void console_fini(void) +{ +#ifdef CONSOLE_SERIAL + (void)serial_fini(); +#endif +} + +static struct class_operations { + struct dev *dev; + int (*probe)(struct dev *dev); + int (*load_configuration)(struct dev *dev); + int (*load)(struct dev *dev); +} +operations[] = { + { &nic.dev, eth_probe, eth_load_configuration, eth_load }, + { &disk.dev, disk_probe, disk_load_configuration, disk_load }, + { &disk.dev, disk_probe, disk_load_configuration, disk_load }, +}; + + +static int main_loop(int state); +static int exit_ok; +static int exit_status; +static int initialized; + +/************************************************************************** +MAIN - Kick off routine +**************************************************************************/ +int main(in_call_data_t *data, va_list params) +{ + char *p; + int state; + + for (p = _bss; p < _ebss; p++) + *p = 0; /* Zero BSS */ + + console_init(); + arch_main(data,params); + +#if 0 +#ifdef CONSOLE_BTEXT + btext_init(); + map_boot_text(); + btext_clearscreen(); +#endif +#endif + + if ( rom.rom_segment ) { + printf ( "ROM segment %#hx length %#hx reloc %#x\n", + rom.rom_segment, rom.rom_length, _text ); + } + + cpu_setup(); + setup_timers(); + gateA20_set(); + print_config(); + get_memsizes(); + cleanup(); + +#ifdef CONFIG_PCMCIA + pcmcia_init_all(); +#endif + + /* -1: timeout or ESC + -2: error return from loader + -3: finish the current run. + 0: retry booting bootp and tftp + 1: retry tftp with possibly modified bootp reply + 2: retry bootp and tftp + 3: retry probe bootp and tftp + 4: start with the next device and retry from there... + 255: exit Etherboot + 256: retry after relocation + */ + state = setjmp(restart_etherboot); + exit_ok = 1; + for(;state != 255;) { + state = main_loop(state); + } + arch_on_exit(exit_status); +#ifdef CONFIG_PCMCIA + pcmcia_shutdown_all(); +#endif + return exit_status; +} + +void exit(int status) +{ + while(!exit_ok) + ; + exit_status = status; + longjmp(restart_etherboot, 255); +} + +static int main_loop(int state) +{ + /* Splitting main into 2 pieces makes the semantics of + * which variables are preserved across a longjmp clean + * and predictable. + */ + static unsigned long order; + static unsigned boot_index; + static struct dev * dev = 0; + static struct class_operations *ops; + static void *heap_base; + static int type; + static int i; + + if (!initialized) { + initialized = 1; + console_init(); + if (dev && (state >= 1) && (state <= 2)) { + dev->how_probe = PROBE_AWAKE; + dev->how_probe = ops->probe(dev); + if (dev->how_probe == PROBE_FAILED) { + state = -1; + } + } + } + switch(state) { + case 0: + { + static int firsttime = 1; + /* First time through */ + if (firsttime) { + relocate(); + cleanup(); + console_init(); + init_heap(); +#ifdef CONSOLE_BTEXT + //I need to all allot + btext_init(); + map_boot_text(); + btext_clearscreen(); +#else + #ifdef CONFIG_FILO + pci_init(); + #endif +#endif + + firsttime = 0; + } +#ifdef EXIT_IF_NO_OFFER + else { + cleanup(); + exit(0); + } +#endif + heap_base = allot(0); + i = -1; + state = 4; + dev = 0; + + /* We just called setjmp ... */ + order = ask_boot(&boot_index); + try_floppy_first(); + break; + } + case 4: + cleanup(); + console_init(); + forget(heap_base); + /* Find a dev entry to probe with */ + if (!dev) { + int boot; + int failsafe; + + /* Advance to the next device type */ + i++; + boot = (order >> (i * BOOT_BITS)) & BOOT_MASK; + type = boot & BOOT_TYPE_MASK; + failsafe = (boot & BOOT_FAILSAFE) != 0; + if (i >= MAX_BOOT_ENTRIES) { + type = BOOT_NOTHING; + } + if ((i == 0) && (type == BOOT_NOTHING)) { + /* Return to caller */ + exit(0); + } + if (type >= BOOT_NOTHING) { + interruptible_sleep(2); + state = 0; + break; + } + ops = &operations[type]; + dev = ops->dev; + dev->how_probe = PROBE_FIRST; + dev->type = type; + dev->failsafe = failsafe; + dev->type_index = 0; + } else { + /* Advance to the next device of the same type */ + dev->how_probe = PROBE_NEXT; + } + state = 3; + break; + case 3: + state = -1; + heap_base = allot(0); + dev->how_probe = ops->probe(dev); + if (dev->how_probe == PROBE_FAILED) { + dev = 0; + state = 4; + } else if (boot_index && (i == 0) && (boot_index != (unsigned)dev->type_index)) { + printf("Wrong index\n"); + state = 4; + } + else { + state = 2; + } + break; + case 2: + state = -1; + if (ops->load_configuration(dev) >= 0) { + state = 1; + } + break; + case 1: + /* Any return from load is a failure */ + ops->load(dev); + state = -1; + break; + case 256: + state = 0; + break; + case -3: + i = MAX_BOOT_ENTRIES; + type = BOOT_NOTHING; + /* fall through */ + default: + printf("\n"); + state = 4; + /* At the end goto state 0 */ + if ((type >= BOOT_NOTHING) || (i >= MAX_BOOT_ENTRIES)) { + state = 0; + } + break; + } + return state; +} + + +/************************************************************************** +LOADKERNEL - Try to load kernel image +**************************************************************************/ +struct proto { + char *name; + int (*load)(const char *name, + int (*fnc)(unsigned char *, unsigned int, unsigned int, int)); +}; +static const struct proto protos[] = { +#ifdef DOWNLOAD_PROTO_TFTM + { "x-tftm", url_tftm }, +#endif +#ifdef DOWNLOAD_PROTO_SLAM + { "x-slam", url_slam }, +#endif +#ifdef DOWNLOAD_PROTO_NFS + { "nfs", nfs }, +#endif +#ifdef DOWNLOAD_PROTO_DISK + { "file", url_file }, +#endif +#ifdef DOWNLOAD_PROTO_TFTP + { "tftp", tftp }, +#endif +#ifdef DOWNLOAD_PROTO_HTTP + { "http", http }, +#endif +}; + +int loadkernel(const char *fname) +{ + static const struct proto * const last_proto = + &protos[sizeof(protos)/sizeof(protos[0])]; + const struct proto *proto; + in_addr ip; + int len; + const char *name; +#ifdef DNS_RESOLVER + const char *resolvt; +#endif + ip.s_addr = arptable[ARP_SERVER].ipaddr.s_addr; + name = fname; + url_port = -1; + len = 0; + while(fname[len] && fname[len] != ':') { + len++; + } + for(proto = &protos[0]; proto < last_proto; proto++) { + if (memcmp(name, proto->name, len) == 0) { + break; + } + } + if ((proto < last_proto) && (memcmp(fname + len, "://", 3) == 0)) { + name += len + 3; + if (name[0] != '/') { +#ifdef DNS_RESOLVER + resolvt = dns_resolver ( name ); + if ( NULL != resolvt ) { + //printf ("Resolved host name [%s] to [%s]\n", + // name, resolvt ); + inet_aton(resolvt, &ip); + while ( ( '/' != name[0] ) && ( 0 != name[0])) + ++name; + } else +#endif /* DNS_RESOLVER */ + name += inet_aton(name, &ip); + if (name[0] == ':') { + name++; + url_port = strtoul(name, &name, 10); + } + } + if (name[0] == '/') { + arptable[ARP_SERVER].ipaddr.s_addr = ip.s_addr; + printf( "Loading %s ", fname ); + return proto->load(name + 1, load_block); + } + } + printf("Loading %@:%s ", arptable[ARP_SERVER].ipaddr, fname); +#ifdef DEFAULT_PROTO_NFS + return nfs(fname, load_block); +#else + return tftp(fname, load_block); +#endif +} + + +/************************************************************************** +CLEANUP - shut down networking and console so that the OS may be called +**************************************************************************/ +void cleanup(void) +{ +#ifdef DOWNLOAD_PROTO_NFS + nfs_umountall(ARP_SERVER); +#endif + /* Stop receiving packets */ + eth_disable(); + disk_disable(); + console_fini(); + initialized = 0; +} + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/src/core/misc.c b/src/core/misc.c new file mode 100644 index 00000000..f5ce9d8f --- /dev/null +++ b/src/core/misc.c @@ -0,0 +1,415 @@ +/************************************************************************** +MISC Support Routines +**************************************************************************/ + +#include "etherboot.h" +#ifdef CONSOLE_BTEXT +#include +#endif +#ifdef CONSOLE_PC_KBD +#include +#endif + + +/************************************************************************** +IPCHKSUM - Checksum IP Header +**************************************************************************/ +uint16_t ipchksum(const void *data, unsigned long length) +{ + unsigned long sum; + unsigned long i; + const uint8_t *ptr; + + /* In the most straight forward way possible, + * compute an ip style checksum. + */ + sum = 0; + ptr = data; + for(i = 0; i < length; i++) { + unsigned long value; + value = ptr[i]; + if (i & 1) { + value <<= 8; + } + /* Add the new value */ + sum += value; + /* Wrap around the carry */ + if (sum > 0xFFFF) { + sum = (sum + (sum >> 16)) & 0xFFFF; + } + } + return (~cpu_to_le16(sum)) & 0xFFFF; +} + +uint16_t add_ipchksums(unsigned long offset, uint16_t sum, uint16_t new) +{ + unsigned long checksum; + sum = ~sum & 0xFFFF; + new = ~new & 0xFFFF; + if (offset & 1) { + /* byte swap the sum if it came from an odd offset + * since the computation is endian independant this + * works. + */ + new = bswap_16(new); + } + checksum = sum + new; + if (checksum > 0xFFFF) { + checksum -= 0xFFFF; + } + return (~checksum) & 0xFFFF; +} + + + +/************************************************************************** +RANDOM - compute a random number between 0 and 2147483647L or 2147483562? +**************************************************************************/ +int32_t random(void) +{ + static int32_t seed = 0; + int32_t q; + if (!seed) /* Initialize linear congruential generator */ + seed = currticks() + *(int32_t *)&arptable[ARP_CLIENT].node + + ((int16_t *)arptable[ARP_CLIENT].node)[2]; + /* simplified version of the LCG given in Bruce Schneier's + "Applied Cryptography" */ + q = seed/53668; + if ((seed = 40014*(seed-53668*q) - 12211*q) < 0) seed += 2147483563L; + return seed; +} + +/************************************************************************** +POLL INTERRUPTIONS +**************************************************************************/ +void poll_interruptions(void) +{ + int ch; + if ( ! as_main_program ) return; + /* If an interruption has occured restart etherboot */ + if (iskey() && (ch = getchar(), (ch == K_ESC) || (ch == K_EOF) || (ch == K_INTR))) { + int state = (ch != K_INTR)? -1 : -3; + longjmp(restart_etherboot, state); + } +} + +/************************************************************************** +SLEEP +**************************************************************************/ +void sleep(int secs) +{ + unsigned long tmo; + + for (tmo = currticks()+secs*TICKS_PER_SEC; currticks() < tmo; ) { + poll_interruptions(); + } +} + +/************************************************************************** +INTERRUPTIBLE SLEEP +**************************************************************************/ +void interruptible_sleep(int secs) +{ + printf("\n"); + return sleep(secs); +} + +/************************************************************************** +TWIDDLE +**************************************************************************/ +void twiddle(void) +{ +#ifdef BAR_PROGRESS + static int count=0; + static const char tiddles[]="-\\|/"; + static unsigned long lastticks = 0; + unsigned long ticks; +#endif + if ( ! as_main_program ) return; +#ifdef BAR_PROGRESS + /* Limit the maximum rate at which characters are printed */ + ticks = currticks(); + if ((lastticks + (TICKS_PER_SEC/18)) > ticks) + return; + lastticks = ticks; + + putchar(tiddles[(count++)&3]); + putchar('\b'); +#else + putchar('.'); +#endif /* BAR_PROGRESS */ +} + +/************************************************************************** +STRCASECMP (not entirely correct, but this will do for our purposes) +**************************************************************************/ +int strcasecmp(const char *a, const char *b) +{ + while (*a && *b && (*a & ~0x20) == (*b & ~0x20)) {a++; b++; } + return((*a & ~0x20) - (*b & ~0x20)); +} + +/************************************************************************** +INET_ATON - Convert an ascii x.x.x.x to binary form +**************************************************************************/ +int inet_aton(const char *start, in_addr *i) +{ + const char *p = start; + 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, &p, 10); + if ((p == digits_start) || (val > 255)) return 0; + if ( ( j < 3 ) && ( *(p++) != '.' ) ) return 0; + ip = (ip << 8) | val; + } + i->s_addr = htonl(ip); + return p - start; +} + + +unsigned long strtoul(const char *p, const char **endp, int base) +{ + unsigned long ret = 0; + if (base != 10) return 0; + while((*p >= '0') && (*p <= '9')) { + ret = ret*10 + (*p - '0'); + p++; + } + if (endp) + *endp = 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 */ + +enum { Disable_A20 = 0x2400, Enable_A20 = 0x2401, Query_A20_Status = 0x2402, + Query_A20_Support = 0x2403 }; + +#if defined(PCBIOS) && !defined(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 */ + +#if defined(PCBIOS) +/* + * Gate A20 for high memory + */ +void gateA20_set(void) +{ +#warning "gateA20_set should test to see if it is already set" + if (int15(Enable_A20) == 0) { + return; + } +#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 */ +} +#endif + + +int last_putchar; // From filo + +void +putchar(int c) +{ + c &= 0xff; + last_putchar = c; + + if (c == '\n') + putchar('\r'); +#ifdef CONSOLE_FIRMWARE + console_putc(c); +#endif +#ifdef CONSOLE_DIRECT_VGA + vga_putc(c); +#endif +#ifdef CONSOLE_BTEXT + btext_putc(c); +#endif +#ifdef CONSOLE_SERIAL + serial_putc(c); +#endif +} + +/************************************************************************** +GETCHAR - Read the next character from input device WITHOUT ECHO +**************************************************************************/ +int getchar(void) +{ + int c = 256; + + do { +#if defined(PCBIOS) && defined(POWERSAVE) + /* 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(); +#endif /* POWERSAVE */ +#ifdef CONSOLE_FIRMWARE + if (console_ischar()) + c = console_getc(); +#endif +#ifdef CONSOLE_SERIAL + if (serial_ischar()) + c = serial_getc(); +#endif +#ifdef CONSOLE_PC_KBD + if (kbd_ischar()) + c = kbd_getc(); +#endif + } while (c==256); + if (c == '\r') + c = '\n'; + return c; +} + +int iskey(void) +{ +#ifdef CONSOLE_FIRMWARE + if (console_ischar()) + return 1; +#endif +#ifdef CONSOLE_SERIAL + if (serial_ischar()) + return 1; +#endif +#ifdef CONSOLE_PC_KBD + if (kbd_ischar()) + return 1; +#endif + return 0; +} + +#if DEBUG_UTILS + +void pause ( void ) { + printf ( "\nPress a key" ); + getchar(); + printf ( "\r \r" ); +} + +void more ( void ) { + printf ( "---more---" ); + getchar(); + printf ( "\r \r" ); +} + +/* Produce a paged hex dump of the specified data and length */ +void hex_dump ( const char *data, const unsigned int len ) { + unsigned int index; + for ( index = 0; index < len; index++ ) { + if ( ( index % 16 ) == 0 ) { + printf ( "\n" ); + } + if ( ( index % 368 ) == 352 ) { + more(); + } + if ( ( index % 16 ) == 0 ) { + printf ( "%X [%X] : %hX :", data + index, + virt_to_phys ( data + index ), index ); + } + printf ( " %hhX", data[index] ); + } + printf ( "\n" ); +} + +#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 %#x-%#x (physical %#x-%#x) " + "corrupted\n", + region, region + len, + virt_to_phys ( region ), + virt_to_phys ( region + len ) ); + } + in_corruption = 1; + printf ( "--- offset %#x ", offset ); + } else if ( ( in_corruption != 0 ) && + ( test == GUARD_SYMBOL ) ) { + /* End of corruption */ + in_corruption = 0; + printf ( "to offset %#x", offset ); + } + + } + if ( in_corruption != 0 ) { + printf ( "to offset %#x (end of region)\n", len-1 ); + } + return corrupted; +} + +#endif /* DEBUG_UTILS */ + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/src/core/nfs.c b/src/core/nfs.c new file mode 100644 index 00000000..cbda6ab7 --- /dev/null +++ b/src/core/nfs.c @@ -0,0 +1,610 @@ +#ifdef DOWNLOAD_PROTO_NFS + +#include "etherboot.h" +#include "nic.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 int mount_port = -1; +static int nfs_port = -1; +static int fs_mounted = 0; +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. */ + printf("RPC error: (%d,%d,%d)\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, struct iphdr *ip, struct udphdr *udp) +{ + 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(int 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(arptable[addr].ipaddr.s_addr, sport, SUNRPC_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 -1; + } else { + return ntohl(rpc->u.reply.data[0]); + } + } + } + return -1; +} + +/************************************************************************** +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(int server, int port, 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(arptable[server].ipaddr.s_addr, sport, 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 { + fs_mounted = 1; + memcpy(fh, rpc->u.reply.data + 1, NFS_FHSIZE); + return 0; + } + } + } + return -1; +} + +/************************************************************************** +NFS_UMOUNTALL - Unmount all our NFS Filesystems on the Server +**************************************************************************/ +void nfs_umountall(int server) +{ + struct rpc_t buf, *rpc; + unsigned long id; + int retries; + long *p; + + if (!arptable[server].ipaddr.s_addr) { + /* Haven't sent a single UDP packet to this server */ + return; + } + if ((mount_port == -1) || (!fs_mounted)) { + /* Nothing mounted, nothing to umount */ + return; + } + 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(arptable[server].ipaddr.s_addr, oport, mount_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); + } + fs_mounted = 0; + return; + } + } +} +/*************************************************************************** + * 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(int server, int port, 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_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(arptable[server].ipaddr.s_addr, sport, 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(int server, int port, 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(arptable[server].ipaddr.s_addr, sport, 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(int server, int port, 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(arptable[server].ipaddr.s_addr, sport, 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 +**************************************************************************/ +int nfs(const char *name, int (*fnc)(unsigned char *, unsigned int, unsigned int, int)) +{ + 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 */ + unsigned int block; + int rlen, size, offs, len; + struct rpc_t *rpc; + + rx_qdrain(); + + sport = oport++; + if (oport > START_OPORT+OPORT_SWEEP) { + oport = START_OPORT; + } + if ( name != dirname ) { + memcpy(dirname, name, namelen + 1); + } + recursion = 0; +nfssymlink: + if ( recursion > NFS_MAXLINKDEPTH ) { + printf ( "\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) { + printf("can't parse file name %s\n", name); + return 0; + } + + if (mount_port == -1) { + mount_port = rpc_lookup(ARP_SERVER, PROG_MOUNT, 1, sport); + } + if (nfs_port == -1) { + nfs_port = rpc_lookup(ARP_SERVER, PROG_NFS, 2, sport); + } + if (nfs_port == -1 || mount_port == -1) { + printf("can't get nfs/mount ports from portmapper\n"); + return 0; + } + + + err = nfs_mount(ARP_SERVER, mount_port, dirname, dirfh, sport); + if (err) { + printf("mounting %s: ", dirname); + nfs_printerror(err); + /* just to be sure... */ + nfs_umountall(ARP_SERVER); + return 0; + } + + err = nfs_lookup(ARP_SERVER, nfs_port, dirfh, fname, filefh, sport); + if (err) { + printf("looking up %s: ", fname); + nfs_printerror(err); + nfs_umountall(ARP_SERVER); + return 0; + } + + offs = 0; + block = 1; /* blocks are numbered starting from 1 */ + size = -1; /* will be set properly with the first reply */ + len = NFS_READ_SIZE; /* first request is always full size */ + do { + err = nfs_read(ARP_SERVER, nfs_port, 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! + block = nfs_readlink(ARP_SERVER, nfs_port, dirfh, dirname, + filefh, sport); + if ( 0 == block ) { + printf("\nLoading symlink:%s ..",dirname); + goto nfssymlink; + } + nfs_printerror(err); + nfs_umountall(ARP_SERVER); + return 0; + } + if (err) { + printf("reading at offset %d: ", offs); + nfs_printerror(err); + nfs_umountall(ARP_SERVER); + 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... */ + } + + err = fnc((char *)&rpc->u.reply.data[19], block, rlen, + (offs+rlen == size)); + if (err <= 0) { + nfs_umountall(ARP_SERVER); + return err; + } + + block++; + 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; +} + +#endif /* DOWNLOAD_PROTO_NFS */ diff --git a/src/core/nic.c b/src/core/nic.c new file mode 100644 index 00000000..44f80c35 --- /dev/null +++ b/src/core/nic.c @@ -0,0 +1,1778 @@ +/************************************************************************** +Etherboot - Network Bootstrap Program + +Literature dealing with the network protocols: + ARP - RFC826 + RARP - RFC903 + IP - RFC791 + UDP - RFC768 + BOOTP - RFC951, RFC2132 (vendor extensions) + DHCP - RFC2131, RFC2132, RFC3004 (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, RFC2113, RFC2365, RFC2236, RFC3171 + +**************************************************************************/ +#include "etherboot.h" +#include "nic.h" +#include "elf.h" /* FOR EM_CURRENT */ + +struct arptable_t arptable[MAX_ARP]; +#if MULTICAST_LEVEL2 +unsigned long last_igmpv1 = 0; +struct igmptable_t igmptable[MAX_IGMP]; +#endif +/* Put rom_info in .nocompress section so romprefix.S can write to it */ +struct rom_info rom __attribute__ ((section (".text16.nocompress"))) = {0,0}; +static unsigned long netmask; +/* Used by nfs.c */ +char *hostname = ""; +int hostnamelen = 0; +static uint32_t xid; +unsigned char *end_of_rfc1533 = NULL; +static int vendorext_isvalid; +static const unsigned char vendorext_magic[] = {0xE4,0x45,0x74,0x68}; /* äEth */ +static const unsigned char broadcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static const in_addr zeroIP = { 0L }; + +struct bootpd_t bootp_data; + +#ifdef NO_DHCP_SUPPORT +static unsigned char rfc1533_cookie[5] = { RFC1533_COOKIE, RFC1533_END }; +#else /* !NO_DHCP_SUPPORT */ +static int dhcp_reply; +static in_addr dhcp_server = { 0L }; +static in_addr dhcp_addr = { 0L }; +static unsigned char rfc1533_cookie[] = { RFC1533_COOKIE }; +#define DHCP_MACHINE_INFO_SIZE (sizeof dhcp_machine_info) +static unsigned char dhcp_machine_info[] = { + /* Our enclosing DHCP tag */ + RFC1533_VENDOR_ETHERBOOT_ENCAP, 11, + /* Our boot device */ + RFC1533_VENDOR_NIC_DEV_ID, 5, PCI_BUS_TYPE, 0, 0, 0, 0, + /* Our current architecture */ + RFC1533_VENDOR_ARCH, 2, EM_CURRENT & 0xff, (EM_CURRENT >> 8) & 0xff, +#ifdef EM_CURRENT_64 + /* The 64bit version of our current architecture */ + RFC1533_VENDOR_ARCH, 2, EM_CURRENT_64 & 0xff, (EM_CURRENT_64 >> 8) & 0xff, +#undef DHCP_MACHINE_INFO_SIZE +#define DHCP_MACHINE_INFO_SIZE (sizeof(dhcp_machine_info) - (EM_CURRENT_64_PRESENT? 0: 4)) +#endif /* EM_CURRENT_64 */ +}; +static const unsigned char dhcpdiscover[] = { + RFC2132_MSG_TYPE,1,DHCPDISCOVER, + RFC2132_MAX_SIZE,2, /* request as much as we can */ + ETH_MAX_MTU / 256, ETH_MAX_MTU % 256, +#ifdef PXE_DHCP_STRICT + RFC3679_PXE_CLIENT_UUID,RFC3679_PXE_CLIENT_UUID_LENGTH,RFC3679_PXE_CLIENT_UUID_DEFAULT, + RFC3679_PXE_CLIENT_ARCH,RFC3679_PXE_CLIENT_ARCH_LENGTH,RFC3679_PXE_CLIENT_ARCH_IAX86PC, + RFC3679_PXE_CLIENT_NDI, RFC3679_PXE_CLIENT_NDI_LENGTH, RFC3679_PXE_CLIENT_NDI_21, + RFC2132_VENDOR_CLASS_ID,RFC2132_VENDOR_CLASS_ID_PXE_LENGTH,RFC2132_VENDOR_CLASS_ID_PXE, +#else + RFC2132_VENDOR_CLASS_ID,13,'E','t','h','e','r','b','o','o','t', + '-',VERSION_MAJOR+'0','.',VERSION_MINOR+'0', +#endif /* PXE_DHCP_STRICT */ +#ifdef DHCP_CLIENT_ID + /* Client ID Option */ + RFC2132_CLIENT_ID, ( DHCP_CLIENT_ID_LEN + 1 ), + DHCP_CLIENT_ID_TYPE, DHCP_CLIENT_ID, +#endif /* DHCP_CLIENT_ID */ +#ifdef DHCP_USER_CLASS + /* User Class Option */ + RFC3004_USER_CLASS, DHCP_USER_CLASS_LEN, DHCP_USER_CLASS, +#endif /* DHCP_USER_CLASS */ + RFC2132_PARAM_LIST, +#define DHCPDISCOVER_PARAMS_BASE 4 +#ifdef PXE_DHCP_STRICT +#define DHCPDISCOVER_PARAMS_PXE ( 1 + 8 ) +#else +#define DHCPDISCOVER_PARAMS_PXE 0 +#endif /* PXE_DHCP_STRICT */ +#ifdef DNS_RESOLVER +#define DHCPDISCOVER_PARAMS_DNS 1 +#else +#define DHCPDISCOVER_PARAMS_DNS 0 +#endif /* DNS_RESOLVER */ + ( DHCPDISCOVER_PARAMS_BASE + + DHCPDISCOVER_PARAMS_PXE+ + DHCPDISCOVER_PARAMS_DNS ), + RFC1533_NETMASK, + RFC1533_GATEWAY, + RFC1533_HOSTNAME, + RFC1533_VENDOR +#ifdef PXE_DHCP_STRICT + ,RFC2132_VENDOR_CLASS_ID, + RFC1533_VENDOR_PXE_OPT128, + RFC1533_VENDOR_PXE_OPT129, + RFC1533_VENDOR_PXE_OPT130, + RFC1533_VENDOR_PXE_OPT131, + RFC1533_VENDOR_PXE_OPT132, + RFC1533_VENDOR_PXE_OPT133, + RFC1533_VENDOR_PXE_OPT134, + RFC1533_VENDOR_PXE_OPT135 +#endif /* PXE_DHCP_STRICT */ +#ifdef DNS_RESOLVER + ,RFC1533_DNS +#endif +}; +static const unsigned 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 */ + ETH_MAX_MTU / 256, ETH_MAX_MTU % 256, +#ifdef PXE_DHCP_STRICT + RFC3679_PXE_CLIENT_UUID,RFC3679_PXE_CLIENT_UUID_LENGTH,RFC3679_PXE_CLIENT_UUID_DEFAULT, + RFC3679_PXE_CLIENT_ARCH,RFC3679_PXE_CLIENT_ARCH_LENGTH,RFC3679_PXE_CLIENT_ARCH_IAX86PC, + RFC3679_PXE_CLIENT_NDI, RFC3679_PXE_CLIENT_NDI_LENGTH, RFC3679_PXE_CLIENT_NDI_21, + RFC2132_VENDOR_CLASS_ID,RFC2132_VENDOR_CLASS_ID_PXE_LENGTH,RFC2132_VENDOR_CLASS_ID_PXE, +#else + RFC2132_VENDOR_CLASS_ID,13,'E','t','h','e','r','b','o','o','t', + '-',VERSION_MAJOR+'0','.',VERSION_MINOR+'0', +#endif /* PXE_DHCP_STRICT */ +#ifdef DHCP_CLIENT_ID + /* Client ID Option */ + RFC2132_CLIENT_ID, ( DHCP_CLIENT_ID_LEN + 1 ), + DHCP_CLIENT_ID_TYPE, DHCP_CLIENT_ID, +#endif /* DHCP_CLIENT_ID */ +#ifdef DHCP_USER_CLASS + /* User Class Option */ + RFC3004_USER_CLASS, DHCP_USER_CLASS_LEN, DHCP_USER_CLASS, +#endif /* DHCP_USER_CLASS */ + /* request parameters */ + RFC2132_PARAM_LIST, +#define DHCPREQUEST_PARAMS_BASE 5 +#ifdef PXE_DHCP_STRICT +#define DHCPREQUEST_PARAMS_PXE 1 +#define DHCPREQUEST_PARAMS_VENDOR_PXE 8 +#define DHCPREQUEST_PARAMS_VENDOR_EB 0 +#else +#define DHCPREQUEST_PARAMS_PXE 0 +#define DHCPREQUEST_PARAMS_VENDOR_PXE 0 +#define DHCPREQUEST_PARAMS_VENDOR_EB 4 +#endif /* PXE_DHCP_STRICT */ +#ifdef IMAGE_FREEBSD +#define DHCPREQUEST_PARAMS_FREEBSD 2 +#else +#define DHCPREQUEST_PARAMS_FREEBSD 0 +#endif /* IMAGE_FREEBSD */ +#ifdef DNS_RESOLVER +#define DHCPREQUEST_PARAMS_DNS 1 +#else +#define DHCPREQUEST_PARAMS_DNS 0 +#endif /* DNS_RESOLVER */ + ( DHCPREQUEST_PARAMS_BASE + + DHCPREQUEST_PARAMS_PXE + + DHCPREQUEST_PARAMS_VENDOR_PXE + + DHCPREQUEST_PARAMS_VENDOR_EB + + DHCPREQUEST_PARAMS_DNS + + DHCPREQUEST_PARAMS_FREEBSD ), + /* 5 Standard parameters */ + RFC1533_NETMASK, + RFC1533_GATEWAY, + RFC1533_HOSTNAME, + RFC1533_VENDOR, + RFC1533_ROOTPATH, /* only passed to the booted image */ +#ifndef PXE_DHCP_STRICT + /* 4 Etherboot vendortags */ + RFC1533_VENDOR_MAGIC, + RFC1533_VENDOR_ADDPARM, + RFC1533_VENDOR_ETHDEV, + RFC1533_VENDOR_ETHERBOOT_ENCAP, +#endif /* ! PXE_DHCP_STRICT */ +#ifdef IMAGE_FREEBSD + /* 2 FreeBSD options */ + RFC1533_VENDOR_HOWTO, + RFC1533_VENDOR_KERNEL_ENV, +#endif +#ifdef DNS_RESOLVER + /* 1 DNS option */ + RFC1533_DNS, +#endif +#ifdef PXE_DHCP_STRICT + RFC2132_VENDOR_CLASS_ID, + RFC1533_VENDOR_PXE_OPT128, + RFC1533_VENDOR_PXE_OPT129, + RFC1533_VENDOR_PXE_OPT130, + RFC1533_VENDOR_PXE_OPT131, + RFC1533_VENDOR_PXE_OPT132, + RFC1533_VENDOR_PXE_OPT133, + RFC1533_VENDOR_PXE_OPT134, + RFC1533_VENDOR_PXE_OPT135, +#endif /* PXE_DHCP_STRICT */ +}; +#ifdef PXE_EXPORT +static const unsigned char proxydhcprequest [] = { + RFC2132_MSG_TYPE,1,DHCPREQUEST, + RFC2132_MAX_SIZE,2, /* request as much as we can */ + ETH_MAX_MTU / 256, ETH_MAX_MTU % 256, +#ifdef PXE_DHCP_STRICT + RFC3679_PXE_CLIENT_UUID,RFC3679_PXE_CLIENT_UUID_LENGTH,RFC3679_PXE_CLIENT_UUID_DEFAULT, + RFC3679_PXE_CLIENT_ARCH,RFC3679_PXE_CLIENT_ARCH_LENGTH,RFC3679_PXE_CLIENT_ARCH_IAX86PC, + RFC3679_PXE_CLIENT_NDI, RFC3679_PXE_CLIENT_NDI_LENGTH, RFC3679_PXE_CLIENT_NDI_21, + RFC2132_VENDOR_CLASS_ID,RFC2132_VENDOR_CLASS_ID_PXE_LENGTH,RFC2132_VENDOR_CLASS_ID_PXE, +#endif /* PXE_DHCP_STRICT */ +}; +#endif + +#ifdef REQUIRE_VCI_ETHERBOOT +int vci_etherboot; +#endif +#endif /* NO_DHCP_SUPPORT */ + +static int dummy(void *unused __unused) +{ + return (0); +} + +/* Careful. We need an aligned buffer to avoid problems on machines + * that care about alignment. To trivally align the ethernet data + * (the ip hdr and arp requests) we offset the packet by 2 bytes. + * leaving the ethernet data 16 byte aligned. Beyond this + * we use memmove but this makes the common cast simple and fast. + */ +static char packet[ETH_FRAME_LEN + ETH_DATA_ALIGN] __aligned; + +struct nic nic = +{ + { + 0, /* dev.disable */ + { + 0, + 0, + PCI_BUS_TYPE, + }, /* dev.devid */ + 0, /* index */ + 0, /* type */ + PROBE_FIRST, /* how_pobe */ + PROBE_NONE, /* to_probe */ + 0, /* failsafe */ + 0, /* type_index */ + {}, /* state */ + }, + (int (*)(struct nic *, int))dummy, /* poll */ + (void (*)(struct nic *, const char *, + unsigned int, unsigned int, + const char *))dummy, /* transmit */ + (void (*)(struct nic *, + irq_action_t))dummy, /* irq */ + 0, /* flags */ + &rom, /* rom_info */ + arptable[ARP_CLIENT].node, /* node_addr */ + packet + ETH_DATA_ALIGN, /* packet */ + 0, /* packetlen */ + 0, /* ioaddr */ + 0, /* irqno */ + 0, /* priv_data */ +}; + +#ifdef RARP_NOT_BOOTP +static int rarp(void); +#else +static int bootp(void); +#endif +static unsigned short tcpudpchksum(struct iphdr *ip); + + +int eth_probe(struct dev *dev) +{ + return probe(dev); +} + +int eth_poll(int retrieve) +{ + return ((*nic.poll)(&nic, retrieve)); +} + +void eth_transmit(const char *d, unsigned int t, unsigned int s, const void *p) +{ + (*nic.transmit)(&nic, d, t, s, p); + if (t == ETH_P_IP) twiddle(); +} + +void eth_disable(void) +{ +#ifdef MULTICAST_LEVEL2 + int i; + for(i = 0; i < MAX_IGMP; i++) { + leave_group(i); + } +#endif + disable(&nic.dev); +} + +void eth_irq (irq_action_t action) +{ + (*nic.irq)(&nic,action); +} + +/* + * Find out what our boot parameters are + */ +int eth_load_configuration(struct dev *dev __unused) +{ + int server_found; + /* Find a server to get BOOTP reply from */ +#ifdef RARP_NOT_BOOTP + printf("Searching for server (RARP)..."); +#else +#ifndef NO_DHCP_SUPPORT + printf("Searching for server (DHCP)..."); +#else + printf("Searching for server (BOOTP)..."); +#endif +#endif + +#ifdef RARP_NOT_BOOTP + server_found = rarp(); +#else + server_found = bootp(); +#endif + if (!server_found) { + printf("No Server found\n"); + longjmp(restart_etherboot, -1); + } + return 0; +} + + +/************************************************************************** +LOAD - Try to get booted +**************************************************************************/ +int eth_load(struct dev *dev __unused) +{ + const char *kernel; + printf("\nMe: %@", arptable[ARP_CLIENT].ipaddr.s_addr ); +#ifndef NO_DHCP_SUPPORT + printf(", DHCP: %@", dhcp_server ); +#ifdef PXE_EXPORT + if (arptable[ARP_PROXYDHCP].ipaddr.s_addr) + printf(" (& %@)", + arptable[ARP_PROXYDHCP].ipaddr.s_addr); +#endif /* PXE_EXPORT */ +#endif /* ! NO_DHCP_SUPPORT */ + printf(", TFTP: %@", arptable[ARP_SERVER].ipaddr.s_addr); + if (BOOTP_DATA_ADDR->bootp_reply.bp_giaddr.s_addr) + printf(", Relay: %@", + BOOTP_DATA_ADDR->bootp_reply.bp_giaddr.s_addr); + if (arptable[ARP_GATEWAY].ipaddr.s_addr) + printf(", Gateway %@", arptable[ARP_GATEWAY].ipaddr.s_addr); +#ifdef DNS_RESOLVER + if (arptable[ARP_NAMESERVER].ipaddr.s_addr) + printf(", Nameserver %@", arptable[ARP_NAMESERVER].ipaddr.s_addr); +#endif + putchar('\n'); + +#ifdef MDEBUG + printf("\n=>>"); getchar(); +#endif + + /* Now use TFTP to load file */ +#ifdef DOWNLOAD_PROTO_NFS + rpc_init(); +#endif + kernel = KERNEL_BUF[0] == '\0' ? +#ifdef DEFAULT_BOOTFILE + DEFAULT_BOOTFILE +#else + NULL +#endif + : KERNEL_BUF; + if ( kernel ) { + loadkernel(kernel); /* We don't return except on error */ + printf("Unable to load file.\n"); + } else { + printf("No filename\n"); + } + interruptible_sleep(2); /* lay off the server for a while */ + longjmp(restart_etherboot, -1); +} + + +/************************************************************************** +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)); +} + +/************************************************************************** +IP_TRANSMIT - Send an IP datagram +**************************************************************************/ +static int await_arp(int ival, void *ptr, + unsigned short ptype, struct iphdr *ip __unused, struct udphdr *udp __unused, + struct tcphdr *tcp __unused) +{ + struct arprequest *arpreply; + if (ptype != ETH_P_ARP) + return 0; + if (nic.packetlen < ETH_HLEN + sizeof(struct arprequest)) + return 0; + arpreply = (struct arprequest *)&nic.packet[ETH_HLEN]; + + if (arpreply->opcode != htons(ARP_REPLY)) + return 0; + if (memcmp(arpreply->sipaddr, ptr, sizeof(in_addr)) != 0) + return 0; + memcpy(arptable[ival].node, arpreply->shwaddr, ETH_ALEN); + return 1; +} + +int ip_transmit(int len, const void *buf) +{ + unsigned long destip; + struct iphdr *ip; + struct arprequest arpreq; + int arpentry, i; + int retry; + + ip = (struct iphdr *)buf; + destip = ip->dest.s_addr; + if (destip == IP_BROADCAST) { + eth_transmit(broadcast, ETH_P_IP, len, buf); +#ifdef MULTICAST_LEVEL1 + } else if ((destip & htonl(MULTICAST_MASK)) == htonl(MULTICAST_NETWORK)) { + unsigned char multicast[6]; + unsigned long hdestip; + hdestip = ntohl(destip); + multicast[0] = 0x01; + multicast[1] = 0x00; + multicast[2] = 0x5e; + multicast[3] = (hdestip >> 16) & 0x7; + multicast[4] = (hdestip >> 8) & 0xff; + multicast[5] = hdestip & 0xff; + eth_transmit(multicast, ETH_P_IP, len, buf); +#endif + } 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; arpentryverhdrlen = 0x45; + ip->verhdrlen += (option_len/4); + ip->service = 0; + ip->len = htons(len); + ip->ident = 0; + ip->frags = 0; /* Should we set don't fragment? */ + ip->ttl = ttl; + ip->protocol = protocol; + ip->chksum = 0; + ip->src.s_addr = arptable[ARP_CLIENT].ipaddr.s_addr; + ip->dest.s_addr = destip; + ip->chksum = ipchksum(buf, sizeof(struct iphdr) + option_len); +} + +void build_udp_hdr(unsigned long destip, + unsigned int srcsock, unsigned int destsock, int ttl, + int len, const void *buf) +{ + struct iphdr *ip; + struct udphdr *udp; + ip = (struct iphdr *)buf; + build_ip_hdr(destip, ttl, IP_UDP, 0, len, buf); + udp = (struct udphdr *)((char *)buf + sizeof(struct iphdr)); + udp->src = htons(srcsock); + udp->dest = htons(destsock); + udp->len = htons(len - sizeof(struct iphdr)); + udp->chksum = 0; + if ((udp->chksum = tcpudpchksum(ip)) == 0) + udp->chksum = 0xffff; +} + +#ifdef DOWNLOAD_PROTO_HTTP +void build_tcp_hdr(unsigned long destip, unsigned int srcsock, + unsigned int destsock, long send_seq, long recv_seq, + int window, int flags, int ttl, int len, const void *buf) +{ + struct iphdr *ip; + struct tcphdr *tcp; + ip = (struct iphdr *)buf; + build_ip_hdr(destip, ttl, IP_TCP, 0, len, buf); + tcp = (struct tcphdr *)(ip + 1); + tcp->src = htons(srcsock); + tcp->dst = htons(destsock); + tcp->seq = htonl(send_seq); + tcp->ack = htonl(recv_seq); + tcp->ctrl = htons(flags + (5 << 12)); /* No TCP options */ + tcp->window = htons(window); + tcp->chksum = 0; + if ((tcp->chksum = tcpudpchksum(ip)) == 0) + tcp->chksum = 0xffff; +} +#endif + + +/************************************************************************** +UDP_TRANSMIT - Send an UDP datagram +**************************************************************************/ +int udp_transmit(unsigned long destip, unsigned int srcsock, + unsigned int destsock, int len, const void *buf) +{ + build_udp_hdr(destip, srcsock, destsock, 60, len, buf); + return ip_transmit(len, buf); +} + +/************************************************************************** +TCP_TRANSMIT - Send a TCP packet +**************************************************************************/ +#ifdef DOWNLOAD_PROTO_HTTP +int tcp_transmit(unsigned long destip, unsigned int srcsock, + unsigned int destsock, long send_seq, long recv_seq, + int window, int flags, int len, const void *buf) +{ + build_tcp_hdr(destip, srcsock, destsock, send_seq, recv_seq, + window, flags, 60, len, buf); + return ip_transmit(len, buf); +} + +int tcp_reset(struct iphdr *ip) { + struct tcphdr *tcp = (struct tcphdr *)(ip + 1); + char buf[sizeof(struct iphdr) + sizeof(struct tcphdr)]; + + if (!(tcp->ctrl & htons(RST))) { + long seq = ntohl(tcp->seq) + ntohs(ip->len) - + sizeof(struct iphdr) - + ((ntohs(tcp->ctrl) >> 10) & 0x3C); + if (tcp->ctrl & htons(SYN|FIN)) + seq++; + return tcp_transmit(ntohl(ip->src.s_addr), + ntohs(tcp->dst), ntohs(tcp->src), + tcp->ctrl&htons(ACK) ? ntohl(tcp->ack) : 0, + seq, TCP_MAX_WINDOW, RST, sizeof(buf), buf); + } + return (1); +} +#endif + +/************************************************************************** +QDRAIN - clear the nic's receive queue +**************************************************************************/ +static int await_qdrain(int ival __unused, void *ptr __unused, + unsigned short ptype __unused, + struct iphdr *ip __unused, struct udphdr *udp __unused, + struct tcphdr *tcp __unused) +{ + return 0; +} + +void rx_qdrain(void) +{ + /* 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); +} + +#ifdef DOWNLOAD_PROTO_TFTP +/************************************************************************** +TFTP - Download extended BOOTP data, or kernel image +**************************************************************************/ +static int await_tftp(int ival, void *ptr __unused, + unsigned short ptype __unused, struct iphdr *ip, struct udphdr *udp, + struct tcphdr *tcp __unused) +{ + if (!udp) { + return 0; + } + if (arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr) + return 0; + if (ntohs(udp->dest) != ival) + return 0; + return 1; +} + +int tftp ( const char *name, + int (*fnc)(unsigned char *, unsigned int, unsigned int, int) ) +{ + struct tftpreq_info_t request_data = + { name, TFTP_PORT, TFTP_MAX_PACKET }; + struct tftpreq_info_t *request = &request_data; + struct tftpblk_info_t block; + int rc; + + while ( tftp_block ( request, &block ) ) { + request = NULL; /* Send request only once */ + rc = fnc ( block.data, block.block, block.len, block.eof ); + if ( rc <= 0 ) return (rc); + if ( block.eof ) { + /* fnc should not have returned */ + printf ( "TFTP download complete, but\n" ); + return (0); + } + } + return (0); +} + +int tftp_block ( struct tftpreq_info_t *request, struct tftpblk_info_t *block ) +{ + static unsigned short lport = 2000; /* local port */ + static unsigned short rport = TFTP_PORT; /* remote port */ + struct tftp_t *rcvd = NULL; + static struct tftpreq_t xmit; + static unsigned short xmitlen = 0; + static unsigned short blockidx = 0; /* Last block received */ + static unsigned short retry = 0; /* Retry attempts on last block */ + static int blksize = 0; + unsigned short recvlen = 0; + + /* If this is a new request (i.e. if name is set), fill in + * transmit block with RRQ and send it. + */ + if ( request ) { + rx_qdrain(); /* Flush receive queue */ + xmit.opcode = htons(TFTP_RRQ); + xmitlen = (void*)&xmit.u.rrq - (void*)&xmit + + sprintf((char*)xmit.u.rrq, "%s%coctet%cblksize%c%d", + request->name, 0, 0, 0, request->blksize) + + 1; /* null terminator */ + blockidx = 0; /* Reset counters */ + retry = 0; + blksize = TFTP_DEFAULTSIZE_PACKET; + lport++; /* Use new local port */ + rport = request->port; + if ( !udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, lport, + rport, xmitlen, &xmit) ) + return (0); + } + /* Exit if no transfer in progress */ + if ( !blksize ) return (0); + /* Loop to wait until we get a packet we're interested in */ + block->data = NULL; /* Used as flag */ + while ( block->data == NULL ) { + long timeout = rfc2131_sleep_interval ( blockidx ? TFTP_REXMT : + TIMEOUT, retry ); + if ( !await_reply(await_tftp, lport, NULL, timeout) ) { + /* No packet received */ + if ( retry++ > MAX_TFTP_RETRIES ) break; + /* Retransmit last packet */ + if ( !blockidx ) lport++; /* New lport if new RRQ */ + if ( !udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, + lport, rport, xmitlen, &xmit) ) + return (0); + continue; /* Back to waiting for packet */ + } + /* Packet has been received */ + rcvd = (struct tftp_t *)&nic.packet[ETH_HLEN]; + recvlen = ntohs(rcvd->udp.len) - sizeof(struct udphdr) + - sizeof(rcvd->opcode); + rport = ntohs(rcvd->udp.src); + retry = 0; /* Reset retry counter */ + switch ( htons(rcvd->opcode) ) { + case TFTP_ERROR : { + printf ( "TFTP error %d (%s)\n", + ntohs(rcvd->u.err.errcode), + rcvd->u.err.errmsg ); + return (0); /* abort */ + } + case TFTP_OACK : { + const char *p = rcvd->u.oack.data; + const char *e = p + recvlen - 10; /* "blksize\0\d\0" */ + + *((char*)(p+recvlen-1)) = '\0'; /* Force final 0 */ + if ( blockidx || !request ) break; /* Too late */ + if ( recvlen <= TFTP_MAX_PACKET ) /* sanity */ { + /* Check for blksize option honoured */ + while ( p < e ) { + if ( strcasecmp("blksize",p) == 0 && + p[7] == '\0' ) { + blksize = strtoul(p+8,&p,10); + p++; /* skip null */ + } + while ( *(p++) ) {}; + } + } + if ( blksize < TFTP_DEFAULTSIZE_PACKET || blksize > request->blksize ) { + /* Incorrect blksize - error and abort */ + xmit.opcode = htons(TFTP_ERROR); + xmit.u.err.errcode = 8; + xmitlen = (void*)&xmit.u.err.errmsg + - (void*)&xmit + + sprintf((char*)xmit.u.err.errmsg, + "RFC1782 error") + + 1; + udp_transmit( + arptable[ARP_SERVER].ipaddr.s_addr, + lport, rport, xmitlen, &xmit); + return (0); + } + } break; + case TFTP_DATA : + if ( ntohs(rcvd->u.data.block) != ( blockidx + 1 ) ) + break; /* Re-ACK last block sent */ + if ( recvlen > ( blksize+sizeof(rcvd->u.data.block) ) ) + break; /* Too large; ignore */ + block->data = rcvd->u.data.download; + block->block = ++blockidx; + block->len = recvlen - sizeof(rcvd->u.data.block); + block->eof = ( (unsigned short)block->len < blksize ); + /* If EOF, zero blksize to indicate transfer done */ + if ( block->eof ) blksize = 0; + break; + default: break; /* Do nothing */ + } + /* Send ACK */ + xmit.opcode = htons(TFTP_ACK); + xmit.u.ack.block = htons(blockidx); + xmitlen = TFTP_MIN_PACKET; + udp_transmit ( arptable[ARP_SERVER].ipaddr.s_addr, + lport, rport, xmitlen, &xmit ); + } + return ( block->data ? 1 : 0 ); +} +#endif /* DOWNLOAD_PROTO_TFTP */ + +#ifdef RARP_NOT_BOOTP +/************************************************************************** +RARP - Get my IP address and load information +**************************************************************************/ +static int await_rarp(int ival, void *ptr, + unsigned short ptype, struct iphdr *ip, struct udphdr *udp, + struct tcphdr *tcp __unused) +{ + struct arprequest *arpreply; + if (ptype != ETH_P_RARP) + return 0; + if (nic.packetlen < ETH_HLEN + sizeof(struct arprequest)) + return 0; + arpreply = (struct arprequest *)&nic.packet[ETH_HLEN]; + if (arpreply->opcode != htons(RARP_REPLY)) + return 0; + if ((arpreply->opcode == htons(RARP_REPLY)) && + (memcmp(arpreply->thwaddr, ptr, ETH_ALEN) == 0)) { + memcpy(arptable[ARP_SERVER].node, arpreply->shwaddr, ETH_ALEN); + memcpy(&arptable[ARP_SERVER].ipaddr, arpreply->sipaddr, sizeof(in_addr)); + memcpy(&arptable[ARP_CLIENT].ipaddr, arpreply->tipaddr, sizeof(in_addr)); + return 1; + } + return 0; +} + +static int rarp(void) +{ + 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 = ETH_ALEN; + rarpreq.protolen = 4; + rarpreq.opcode = htons(RARP_REQUEST); + memcpy(&rarpreq.shwaddr, arptable[ARP_CLIENT].node, ETH_ALEN); + /* sipaddr is already zeroed out */ + memcpy(&rarpreq.thwaddr, arptable[ARP_CLIENT].node, ETH_ALEN); + /* tipaddr is already zeroed out */ + + for (retry = 0; retry < MAX_ARP_RETRIES; ++retry) { + long timeout; + eth_transmit(broadcast, ETH_P_RARP, sizeof(rarpreq), &rarpreq); + + timeout = rfc2131_sleep_interval(TIMEOUT, retry); + if (await_reply(await_rarp, 0, rarpreq.shwaddr, timeout)) + break; + } + + if (retry < MAX_ARP_RETRIES) { + (void)sprintf(KERNEL_BUF, DEFAULT_KERNELPATH, arptable[ARP_CLIENT].ipaddr); + + return (1); + } + return (0); +} + +#else + +/************************************************************************** +BOOTP - Get my IP address and load information +**************************************************************************/ +static int await_bootp(int ival __unused, void *ptr __unused, + unsigned short ptype __unused, struct iphdr *ip __unused, + struct udphdr *udp, struct tcphdr *tcp __unused) +{ + struct bootp_t *bootpreply; + if (!udp) { + return 0; + } + bootpreply = (struct bootp_t *)&nic.packet[ETH_HLEN + + sizeof(struct iphdr) + sizeof(struct udphdr)]; + if (nic.packetlen < ETH_HLEN + sizeof(struct iphdr) + + sizeof(struct udphdr) + +#ifdef NO_DHCP_SUPPORT + sizeof(struct bootp_t) +#else + sizeof(struct bootp_t) - DHCP_OPT_LEN +#endif /* NO_DHCP_SUPPORT */ + ) { + return 0; + } + if (udp->dest != htons(BOOTP_CLIENT)) + return 0; + if (bootpreply->bp_op != BOOTP_REPLY) + return 0; + if (bootpreply->bp_xid != xid) + return 0; + if (memcmp(&bootpreply->bp_siaddr, &zeroIP, sizeof(in_addr)) == 0) + return 0; + if ((memcmp(broadcast, bootpreply->bp_hwaddr, ETH_ALEN) != 0) && + (memcmp(arptable[ARP_CLIENT].node, bootpreply->bp_hwaddr, ETH_ALEN) != 0)) { + return 0; + } + if ( bootpreply->bp_siaddr.s_addr ) { + arptable[ARP_SERVER].ipaddr.s_addr = bootpreply->bp_siaddr.s_addr; + memset(arptable[ARP_SERVER].node, 0, ETH_ALEN); /* Kill arp */ + } + if ( bootpreply->bp_giaddr.s_addr ) { + arptable[ARP_GATEWAY].ipaddr.s_addr = bootpreply->bp_giaddr.s_addr; + memset(arptable[ARP_GATEWAY].node, 0, ETH_ALEN); /* Kill arp */ + } + if (bootpreply->bp_yiaddr.s_addr) { + /* Offer with an IP address */ + 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(); + /* bootpreply->bp_file will be copied to KERNEL_BUF in the memcpy */ + memcpy((char *)BOOTP_DATA_ADDR, (char *)bootpreply, sizeof(struct bootpd_t)); + decode_rfc1533(BOOTP_DATA_ADDR->bootp_reply.bp_vend, 0, +#ifdef NO_DHCP_SUPPORT + BOOTP_VENDOR_LEN + MAX_BOOTP_EXTLEN, +#else + DHCP_OPT_LEN + MAX_BOOTP_EXTLEN, +#endif /* NO_DHCP_SUPPORT */ + 1); +#ifdef PXE_EXPORT + } else { + /* Offer without an IP address - use as ProxyDHCP server */ + arptable[ARP_PROXYDHCP].ipaddr.s_addr = bootpreply->bp_siaddr.s_addr; + memset(arptable[ARP_PROXYDHCP].node, 0, ETH_ALEN); /* Kill arp */ + /* Grab only the bootfile name from a ProxyDHCP packet */ + memcpy(KERNEL_BUF, bootpreply->bp_file, sizeof(KERNEL_BUF)); +#endif /* PXE_EXPORT */ + } +#ifdef REQUIRE_VCI_ETHERBOOT + if (!vci_etherboot) + return (0); +#endif + return(1); +} + +static int bootp(void) +{ + int retry; +#ifndef NO_DHCP_SUPPORT + int reqretry; +#endif /* NO_DHCP_SUPPORT */ + struct bootpip_t ip; + unsigned long starttime; + unsigned char *bp_vend; + +#ifndef NO_DHCP_SUPPORT + dhcp_machine_info[4] = nic.dev.devid.bus_type; + dhcp_machine_info[5] = nic.dev.devid.vendor_id & 0xff; + dhcp_machine_info[6] = ((nic.dev.devid.vendor_id) >> 8) & 0xff; + dhcp_machine_info[7] = nic.dev.devid.device_id & 0xff; + dhcp_machine_info[8] = ((nic.dev.devid.device_id) >> 8) & 0xff; +#endif /* NO_DHCP_SUPPORT */ + memset(&ip, 0, sizeof(struct bootpip_t)); + ip.bp.bp_op = BOOTP_REQUEST; + ip.bp.bp_htype = 1; + ip.bp.bp_hlen = ETH_ALEN; + starttime = currticks(); + /* Use lower 32 bits of node address, more likely to be + distinct than the time since booting */ + memcpy(&xid, &arptable[ARP_CLIENT].node[2], sizeof(xid)); + ip.bp.bp_xid = xid += htonl(starttime); + memcpy(ip.bp.bp_hwaddr, arptable[ARP_CLIENT].node, ETH_ALEN); +#ifdef NO_DHCP_SUPPORT + memcpy(ip.bp.bp_vend, rfc1533_cookie, 5); /* request RFC-style options */ +#else + memcpy(ip.bp.bp_vend, rfc1533_cookie, sizeof rfc1533_cookie); /* request RFC-style options */ + memcpy(ip.bp.bp_vend + sizeof rfc1533_cookie, dhcpdiscover, sizeof dhcpdiscover); + /* Append machine_info to end, in encapsulated option */ + bp_vend = ip.bp.bp_vend + sizeof rfc1533_cookie + sizeof dhcpdiscover; + memcpy(bp_vend, dhcp_machine_info, DHCP_MACHINE_INFO_SIZE); + bp_vend += DHCP_MACHINE_INFO_SIZE; + *bp_vend++ = RFC1533_END; +#endif /* NO_DHCP_SUPPORT */ + + for (retry = 0; retry < MAX_BOOTP_RETRIES; ) { + uint8_t my_hwaddr[ETH_ALEN]; + unsigned long stop_time; + long remaining_time; + + rx_qdrain(); + + /* Kill arptable to avoid keeping stale entries */ + memcpy ( my_hwaddr, arptable[ARP_CLIENT].node, ETH_ALEN ); + memset ( arptable, 0, sizeof(arptable) ); + memcpy ( arptable[ARP_CLIENT].node, my_hwaddr, ETH_ALEN ); + + udp_transmit(IP_BROADCAST, BOOTP_CLIENT, BOOTP_SERVER, + sizeof(struct bootpip_t), &ip); + remaining_time = rfc2131_sleep_interval(BOOTP_TIMEOUT, retry++); + stop_time = currticks() + remaining_time; +#ifdef NO_DHCP_SUPPORT + if (await_reply(await_bootp, 0, NULL, timeout)) + return(1); +#else + while ( remaining_time > 0 ) { + if (await_reply(await_bootp, 0, NULL, remaining_time)){ + } + remaining_time = stop_time - currticks(); + } + if ( ! arptable[ARP_CLIENT].ipaddr.s_addr ) { + printf("No IP address\n"); + continue; + } + /* If not a DHCPOFFER then must be just a BOOTP reply, + * be backward compatible with BOOTP then */ + if (dhcp_reply != DHCPOFFER) + return(1); + dhcp_reply = 0; + /* Construct the DHCPREQUEST packet */ + memcpy(ip.bp.bp_vend, rfc1533_cookie, sizeof rfc1533_cookie); + memcpy(ip.bp.bp_vend + sizeof rfc1533_cookie, dhcprequest, sizeof dhcprequest); + /* Beware: the magic numbers 9 and 15 depend on + the layout of dhcprequest */ + memcpy(&ip.bp.bp_vend[9], &dhcp_server, sizeof(in_addr)); + memcpy(&ip.bp.bp_vend[15], &dhcp_addr, sizeof(in_addr)); + bp_vend = ip.bp.bp_vend + sizeof rfc1533_cookie + sizeof dhcprequest; + /* Append machine_info to end, in encapsulated option */ + memcpy(bp_vend, dhcp_machine_info, DHCP_MACHINE_INFO_SIZE); + bp_vend += DHCP_MACHINE_INFO_SIZE; + *bp_vend++ = RFC1533_END; + for (reqretry = 0; reqretry < MAX_BOOTP_RETRIES; ) { + unsigned long timeout; + + udp_transmit(IP_BROADCAST, BOOTP_CLIENT, BOOTP_SERVER, + sizeof(struct bootpip_t), &ip); + dhcp_reply=0; + timeout = rfc2131_sleep_interval(TIMEOUT, reqretry++); + if (!await_reply(await_bootp, 0, NULL, timeout)) + continue; + if (dhcp_reply != DHCPACK) + continue; + dhcp_reply = 0; +#ifdef PXE_EXPORT + if ( arptable[ARP_PROXYDHCP].ipaddr.s_addr ) { + /* Construct the ProxyDHCPREQUEST packet */ + memcpy(ip.bp.bp_vend, rfc1533_cookie, sizeof rfc1533_cookie); + memcpy(ip.bp.bp_vend + sizeof rfc1533_cookie, proxydhcprequest, sizeof proxydhcprequest); + for (reqretry = 0; reqretry < MAX_BOOTP_RETRIES; ) { + printf ( "\nSending ProxyDHCP request to %@...", arptable[ARP_PROXYDHCP].ipaddr.s_addr); + udp_transmit(arptable[ARP_PROXYDHCP].ipaddr.s_addr, BOOTP_CLIENT, PROXYDHCP_SERVER, + sizeof(struct bootpip_t), &ip); + timeout = rfc2131_sleep_interval(TIMEOUT, reqretry++); + if (await_reply(await_bootp, 0, NULL, timeout)) { + break; + } + } + } +#endif /* PXE_EXPORT */ + return(1); + } +#endif /* NO_DHCP_SUPPORT */ + ip.bp.bp_secs = htons((currticks()-starttime)/TICKS_PER_SEC); + } + return(0); +} +#endif /* RARP_NOT_BOOTP */ + +static uint16_t tcpudpchksum(struct iphdr *ip) +{ + struct udp_pseudo_hdr pseudo; + uint16_t checksum; + + /* Compute the pseudo header */ + pseudo.src.s_addr = ip->src.s_addr; + pseudo.dest.s_addr = ip->dest.s_addr; + pseudo.unused = 0; + pseudo.protocol = ip->protocol; + pseudo.len = htons(ntohs(ip->len) - sizeof(struct iphdr)); + + /* Sum the pseudo header */ + checksum = ipchksum(&pseudo, 12); + + /* Sum the rest of the tcp/udp packet */ + checksum = add_ipchksums(12, checksum, ipchksum(ip + 1, + ntohs(ip->len) - sizeof(struct iphdr))); + return checksum; +} + +#ifdef MULTICAST_LEVEL2 +static void send_igmp_reports(unsigned long now) +{ + int i; + for(i = 0; i < MAX_IGMP; i++) { + if (igmptable[i].time && (now >= igmptable[i].time)) { + 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(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); +#ifdef MDEBUG + printf("Sent IGMP report to: %@\n", igmp.igmp.group.s_addr); +#endif + /* Don't send another igmp report until asked */ + igmptable[i].time = 0; + } + } +} + +static void process_igmp(struct iphdr *ip, unsigned long now) +{ + 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; + } + +#ifdef MDEBUG + printf("Received IGMP query for: %@\n", igmp->group.s_addr); +#endif + 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)) { +#ifdef MDEBUG + printf("Received IGMP report for: %@\n", igmp->group.s_addr); +#endif + 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; + } + } + } +} + +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); +#ifdef MDEBUG + printf("Sent IGMP leave for: %@\n", igmp.igmp.group.s_addr); +#endif + } + 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(); + } +} +#else +#define send_igmp_reports(now) do {} while(0) +#define process_igmp(ip, now) do {} while(0) +#endif + +#include "proto_eth_slow.c" + +/************************************************************************** +TCP - Simple-minded TCP stack. Can only send data once and then + receive the response. The algorithm for computing window + sizes and delaying ack's is currently broken, and thus + disabled. Performance would probably improve a little, if + this gets fixed. FIXME +**************************************************************************/ +#ifdef DOWNLOAD_PROTO_HTTP +static int await_tcp(int ival, void *ptr, unsigned short ptype __unused, + struct iphdr *ip, struct udphdr *udp __unused, + struct tcphdr *tcp) +{ + if (!tcp) { + return 0; + } + if (arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr) + return 0; + if (ntohs(tcp->dst) != ival) { + tcp_reset(ip); + return 0; + } + *(void **)ptr = tcp; + return 1; +} + +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)) { + static uint16_t srcsock = 0; + int rc = 1; + long send_seq = currticks(); + long recv_seq = 0; + int can_send = 0; + int sent_all = 0; + struct iphdr *ip; + struct tcphdr *tcp; + int ctrl = SYN; + char buf[128]; /* Small outgoing buffer */ + long payload; + int header_size; + int window = 3*TCP_MIN_WINDOW; + long last_ack = 0; + long last_sent = 0; + long rtt = 0; + long srtt = 0; + long rto = TCP_INITIAL_TIMEOUT; + int retry = TCP_MAX_TIMEOUT/TCP_INITIAL_TIMEOUT; + enum { CLOSED, SYN_RCVD, ESTABLISHED, + FIN_WAIT_1, FIN_WAIT_2 } state = CLOSED; + + if (!srcsock) { + srcsock = currticks(); + } + if (++srcsock < 1024) + srcsock += 1024; + + await_reply(await_qdrain, 0, NULL, 0); + + send_data: + if (ctrl & ACK) + last_ack = recv_seq; + if (!tcp_transmit(destip, srcsock, destsock, send_seq, + recv_seq, window, ctrl, + sizeof(struct iphdr) + sizeof(struct tcphdr)+ + can_send, buf)) { + return (0); + } + last_sent = currticks(); + + recv_data: + if (!await_reply(await_tcp, srcsock, &tcp, + (state == ESTABLISHED && !can_send) + ? TCP_MAX_TIMEOUT : rto)) { + if (state == ESTABLISHED) { + close: + ctrl = FIN|ACK; + state = FIN_WAIT_1; + rc = 0; + goto send_data; + } + + if (state == FIN_WAIT_1 || state == FIN_WAIT_2) + return (rc); + + if (--retry <= 0) { + /* time out */ + if (state == SYN_RCVD) { + tcp_transmit(destip, srcsock, destsock, + send_seq, 0, window, RST, + sizeof(struct iphdr) + + sizeof(struct tcphdr), buf); + } + return (0); + } + /* retransmit */ + goto send_data; + } + got_data: + retry = TCP_MAX_RETRY; + + if (tcp->ctrl & htons(ACK) ) { + char *data; + int syn_ack, consumed; + + if (state == FIN_WAIT_1 || state == FIN_WAIT_2) { + state = FIN_WAIT_2; + ctrl = ACK; + goto consume_data; + } + syn_ack = state == CLOSED || state == SYN_RCVD; + consumed = ntohl(tcp->ack) - send_seq - syn_ack; + if (consumed < 0 || consumed > can_send) { + tcp_reset((struct iphdr *)&nic.packet[ETH_HLEN]); + goto recv_data; + } + + rtt = currticks() - last_sent; + srtt = !srtt ? rtt : (srtt*4 + rtt)/5; + rto = srtt + srtt/2; + if (rto < TCP_MIN_TIMEOUT) + rto = TCP_MIN_TIMEOUT; + else if (rto > TCP_MAX_TIMEOUT) + rto = TCP_MAX_TIMEOUT; + + can_send -= consumed; + send_seq += consumed + syn_ack; + data = buf + sizeof(struct iphdr) + sizeof(struct tcphdr); + if (can_send) { + memmove(data, data + consumed, can_send); + } + if (!sent_all) { + int more_data; + data += can_send; + more_data = buf + sizeof(buf) - data; + if (more_data > 0) { + more_data = send(more_data, data, ptr); + can_send += more_data; + } + sent_all = !more_data; + } + if (state == SYN_RCVD) { + state = ESTABLISHED; + ctrl = PSH|ACK; + goto consume_data; + } + if (tcp->ctrl & htons(RST)) + return (0); + } else if (tcp->ctrl & htons(RST)) { + if (state == CLOSED) + goto recv_data; + return (0); + } + + consume_data: + ip = (struct iphdr *)&nic.packet[ETH_HLEN]; + header_size = sizeof(struct iphdr) + ((ntohs(tcp->ctrl)>>10)&0x3C); + payload = ntohs(ip->len) - header_size; + if (payload > 0 && state == ESTABLISHED) { + int old_bytes = recv_seq - (long)ntohl(tcp->seq); + if (old_bytes >= 0 && payload - old_bytes > 0) { + recv_seq += payload - old_bytes; + if (state != FIN_WAIT_1 && state != FIN_WAIT_2 && + !recv(payload - old_bytes, + &nic.packet[ETH_HLEN+header_size+old_bytes], + ptr)) { + goto close; + } + if ((state == ESTABLISHED || state == SYN_RCVD) && + !(tcp->ctrl & htons(FIN))) { + int in_window = window - 2*TCP_MIN_WINDOW > + recv_seq - last_ack; + ctrl = can_send ? PSH|ACK : ACK; + if (!can_send && in_window) { +/* Window scaling is broken right now, just fall back to acknowledging every */ +/* packet immediately and unconditionally. FIXME */ /***/ +/* if (await_reply(await_tcp, srcsock, + &tcp, rto)) + goto got_data; + else */ + goto send_data; + } + if (!in_window) { + window += TCP_MIN_WINDOW; + if (window > TCP_MAX_WINDOW) + window = TCP_MAX_WINDOW; + } + goto send_data; + } + } else { + /* saw old data again, must have lost packets */ + window /= 2; + if (window < 2*TCP_MIN_WINDOW) + window = 2*TCP_MIN_WINDOW; + } + } + + if (tcp->ctrl & htons(FIN)) { + if (state == ESTABLISHED) { + ctrl = FIN|ACK; + } else if (state == FIN_WAIT_1 || state == FIN_WAIT_2) { + ctrl = ACK; + } else { + ctrl = RST; + } + return (tcp_transmit(destip, srcsock, destsock, + send_seq, recv_seq + 1, window, ctrl, + sizeof(struct iphdr) + + sizeof(struct tcphdr), buf) && + (state == ESTABLISHED || + state == FIN_WAIT_1 || state == FIN_WAIT_2) && + !can_send); + } + + if (state == CLOSED) { + if (tcp->ctrl & htons(SYN)) { + recv_seq = ntohl(tcp->seq) + 1; + if (!(tcp->ctrl & htons(ACK))) { + state = SYN_RCVD; + ctrl = SYN|ACK|PSH; + goto send_data; + } else { + state = ESTABLISHED; + ctrl = PSH|ACK; + } + } + } + + if (can_send || payload) { + goto send_data; + } + goto recv_data; +} +#endif + +/************************************************************************** +AWAIT_REPLY - Wait until we get a response for our request +************f**************************************************************/ +int await_reply(reply_t reply, int ival, void *ptr, long timeout) +{ + unsigned long time, now; + struct iphdr *ip; + unsigned iplen = 0; + struct udphdr *udp; + struct tcphdr *tcp; + unsigned short ptype; + int result; + + 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 (;;) { + now = currticks(); + send_eth_slow_reports(now); + send_igmp_reports(now); + result = eth_poll(1); + if (result == 0) { + /* We don't have anything */ + + /* 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. */ + poll_interruptions(); + /* Do the timeout after at least a full queue walk. */ + if ((timeout == 0) || (currticks() > time)) { + break; + } + continue; + } + + /* We have something! */ + + /* Find the Ethernet packet type */ + if (nic.packetlen >= ETH_HLEN) { + ptype = ((unsigned short) nic.packet[12]) << 8 + | ((unsigned short) nic.packet[13]); + } else continue; /* what else could we do with it? */ + /* Verify an IP header */ + ip = 0; + if ((ptype == ETH_P_IP) && (nic.packetlen >= ETH_HLEN + sizeof(struct iphdr))) { + unsigned ipoptlen; + ip = (struct iphdr *)&nic.packet[ETH_HLEN]; + if ((ip->verhdrlen < 0x45) || (ip->verhdrlen > 0x4F)) + continue; + iplen = (ip->verhdrlen & 0xf) * 4; + if (ipchksum(ip, iplen) != 0) + continue; + if (ip->frags & htons(0x3FFF)) { + static int warned_fragmentation = 0; + if (!warned_fragmentation) { + printf("ALERT: got a fragmented packet - reconfigure your server\n"); + warned_fragmentation = 1; + } + continue; + } + if (ntohs(ip->len) > ETH_MAX_MTU) + continue; + + ipoptlen = iplen - sizeof(struct iphdr); + if (ipoptlen) { + /* Delete the ip options, to guarantee + * good alignment, and make etherboot simpler. + */ + memmove(&nic.packet[ETH_HLEN + sizeof(struct iphdr)], + &nic.packet[ETH_HLEN + iplen], + nic.packetlen - ipoptlen); + nic.packetlen -= ipoptlen; + } + } + udp = 0; + if (ip && (ip->protocol == IP_UDP) && + (nic.packetlen >= + ETH_HLEN + sizeof(struct iphdr) + sizeof(struct udphdr))) { + udp = (struct udphdr *)&nic.packet[ETH_HLEN + sizeof(struct iphdr)]; + + /* Make certain we have a reasonable packet length */ + if (ntohs(udp->len) > (ntohs(ip->len) - iplen)) + continue; + + if (udp->chksum && tcpudpchksum(ip)) { + printf("UDP checksum error\n"); + continue; + } + } + tcp = 0; +#ifdef DOWNLOAD_PROTO_HTTP + if (ip && (ip->protocol == IP_TCP) && + (nic.packetlen >= + ETH_HLEN + sizeof(struct iphdr) + sizeof(struct tcphdr))){ + tcp = (struct tcphdr *)&nic.packet[ETH_HLEN + + sizeof(struct iphdr)]; + /* Make certain we have a reasonable packet length */ + if (((ntohs(tcp->ctrl) >> 10) & 0x3C) > + ntohs(ip->len) - (int)iplen) + continue; + if (tcpudpchksum(ip)) { + printf("TCP checksum error\n"); + continue; + } + + } +#endif + result = reply(ival, ptr, ptype, ip, udp, tcp); + if (result > 0) { + return result; + } + + /* If it isn't a packet the upper layer wants see if there is a default + * action. This allows us reply to arp, igmp, and lacp queries. + */ + if ((ptype == ETH_P_ARP) && + (nic.packetlen >= ETH_HLEN + sizeof(struct arprequest))) { + struct arprequest *arpreply; + unsigned long tmp; + + arpreply = (struct arprequest *)&nic.packet[ETH_HLEN]; + memcpy(&tmp, arpreply->tipaddr, sizeof(in_addr)); + if ((arpreply->opcode == htons(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, ETH_ALEN); + memcpy(arpreply->sipaddr, &arptable[ARP_CLIENT].ipaddr, sizeof(in_addr)); + memcpy(arpreply->shwaddr, arptable[ARP_CLIENT].node, ETH_ALEN); + eth_transmit(arpreply->thwaddr, ETH_P_ARP, + sizeof(struct arprequest), + arpreply); +#ifdef MDEBUG + memcpy(&tmp, arpreply->tipaddr, sizeof(in_addr)); + printf("Sent ARP reply to: %@\n",tmp); +#endif /* MDEBUG */ + } + } + process_eth_slow(ptype, now); + process_igmp(ip, now); + } + return(0); +} + +#ifdef REQUIRE_VCI_ETHERBOOT +/************************************************************************** +FIND_VCI_ETHERBOOT - Looks for "Etherboot" in Vendor Encapsulated Identifiers +On entry p points to byte count of VCI options +**************************************************************************/ +static int find_vci_etherboot(unsigned char *p) +{ + unsigned char *end = p + 1 + *p; + + for (p++; p < end; ) { + if (*p == RFC2132_VENDOR_CLASS_ID) { + if (strncmp("Etherboot", p + 2, sizeof("Etherboot") - 1) == 0) + return (1); + } else if (*p == RFC1533_END) + return (0); + p += TAG_LEN(p) + 2; + } + return (0); +} +#endif /* REQUIRE_VCI_ETHERBOOT */ + +/************************************************************************** +DECODE_RFC1533 - Decodes RFC1533 header +**************************************************************************/ +int decode_rfc1533(unsigned char *p, unsigned int block, unsigned int len, int eof) +{ + static unsigned char *extdata = NULL, *extend = NULL; + unsigned char *extpath = NULL; + unsigned char *endp; + static unsigned char in_encapsulated_options = 0; + + if (eof == -1) { + /* Encapsulated option block */ + endp = p + len; + } + else if (block == 0) { +#ifdef REQUIRE_VCI_ETHERBOOT + vci_etherboot = 0; +#endif + end_of_rfc1533 = NULL; +#ifdef IMAGE_FREEBSD + /* yes this is a pain FreeBSD uses this for swap, however, + there are cases when you don't want swap and then + you want this set to get the extra features so lets + just set if dealing with FreeBSD. I haven't run into + any troubles with this but I have without it + */ + vendorext_isvalid = 1; +#ifdef FREEBSD_KERNEL_ENV + memcpy(freebsd_kernel_env, FREEBSD_KERNEL_ENV, + sizeof(FREEBSD_KERNEL_ENV)); + /* FREEBSD_KERNEL_ENV had better be a string constant */ +#else + freebsd_kernel_env[0]='\0'; +#endif +#else + vendorext_isvalid = 0; +#endif + 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) + return 1; + 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 (NON_ENCAP_OPT c == RFC1533_NETMASK) + memcpy(&netmask, p+2, sizeof(in_addr)); + else if (NON_ENCAP_OPT 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 +#ifdef REQUIRE_VCI_ETHERBOOT + else if (NON_ENCAP_OPT c == RFC1533_VENDOR) { + vci_etherboot = find_vci_etherboot(p+1); +#ifdef MDEBUG + printf("vci_etherboot %d\n", vci_etherboot); +#endif + } +#endif /* REQUIRE_VCI_ETHERBOOT */ + else if (NON_ENCAP_OPT c == RFC2132_MSG_TYPE) + dhcp_reply=*(p+2); + else if (NON_ENCAP_OPT c == RFC2132_SRV_ID) + memcpy(&dhcp_server, p+2, sizeof(in_addr)); +#endif /* NO_DHCP_SUPPORT */ + else if (NON_ENCAP_OPT c == RFC1533_HOSTNAME) { + hostname = p + 2; + hostnamelen = *(p + 1); + } + else if (ENCAP_OPT c == RFC1533_VENDOR_MAGIC + && TAG_LEN(p) >= 6 && + !memcmp(p+2,vendorext_magic,4) && + p[6] == RFC1533_VENDOR_MAJOR + ) + vendorext_isvalid++; + else if (NON_ENCAP_OPT c == RFC1533_VENDOR_ETHERBOOT_ENCAP) { + in_encapsulated_options = 1; + decode_rfc1533(p+2, 0, TAG_LEN(p), -1); + in_encapsulated_options = 0; + } +#ifdef IMAGE_FREEBSD + else if (NON_ENCAP_OPT c == RFC1533_VENDOR_HOWTO) + freebsd_howto = ((p[2]*256+p[3])*256+p[4])*256+p[5]; + else if (NON_ENCAP_OPT c == RFC1533_VENDOR_KERNEL_ENV){ + if(*(p + 1) < sizeof(freebsd_kernel_env)){ + memcpy(freebsd_kernel_env,p+2,*(p+1)); + }else{ + printf("Only support %ld bytes in Kernel Env\n", + sizeof(freebsd_kernel_env)); + } + } +#endif +#ifdef DNS_RESOLVER + else if (NON_ENCAP_OPT c == RFC1533_DNS) { + // TODO: Copy the DNS IP somewhere reasonable + if (TAG_LEN(p) >= sizeof(in_addr)) + memcpy(&arptable[ARP_NAMESERVER].ipaddr, p+2, sizeof(in_addr)); + } +#endif + else { +#if 0 + unsigned char *q; + printf("Unknown RFC1533-tag "); + for(q=p;q BACKOFF_LIMIT) + exp = BACKOFF_LIMIT; +#endif + tmo = (base << exp) + (TICKS_PER_SEC - (random()/TWO_SECOND_DIVISOR)); + return tmo; +} + +#ifdef MULTICAST_LEVEL2 +/************************************************************************** +RFC1112_SLEEP_INTERVAL - sleep for expotentially longer times, up to (base << exp) +**************************************************************************/ +long rfc1112_sleep_interval(long base, int exp) +{ + unsigned long divisor, tmo; +#ifdef BACKOFF_LIMIT + if (exp > BACKOFF_LIMIT) + exp = BACKOFF_LIMIT; +#endif + divisor = RAND_MAX/(base << exp); + tmo = random()/divisor; + return tmo; +} +#endif /* MULTICAST_LEVEL_2 */ diff --git a/src/core/osloader.c b/src/core/osloader.c new file mode 100644 index 00000000..ae67b34d --- /dev/null +++ b/src/core/osloader.c @@ -0,0 +1,365 @@ +/************************************************************************** +OS loader + +Author: Markus Gutschke (gutschk@math.uni-muenster.de) + Date: Sep/95 +Modifications: Ken Yap (for Etherboot/16) + Doug Ambrisko (ELF and a.out support) + Klaus Espenlaub (rewrote ELF and a.out (did it really work before?) support, + added ELF Multiboot images). Someone should merge the ELF and a.out + loaders, as most of the code is now identical. Maybe even NBI could be + rewritten and merged into the generic loading framework. This should + save quite a few bytes of code if you have selected more than one format. + Ken Yap (Jan 2001) + Added support for linear entry addresses in tagged images, + which allows a more efficient protected mode call instead of + going to real mode and back. Also means entry addresses > 1 MB can + be called. Conditional on the LINEAR_EXEC_ADDR bit. + Added support for Etherboot extension calls. Conditional on the + TAGGED_PROGRAM_RETURNS bit. Implies LINEAR_EXEC_ADDR. + Added support for non-MULTIBOOT ELF which also supports Etherboot + extension calls. Conditional on the ELF_PROGRAM_RETURNS bit. + +**************************************************************************/ + +/* + * 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" + +struct os_entry_regs os_regs; + +static struct ebinfo loaderinfo = { + VERSION_MAJOR, VERSION_MINOR, + 0 +}; + +#define LOAD_DEBUG 0 + +static int prep_segment(unsigned long start, unsigned long mid, unsigned long end, + unsigned long istart, unsigned long iend); +static unsigned long find_segment(unsigned long size, unsigned long align); +static sector_t dead_download ( unsigned char *data, unsigned int len, int eof); +static void done(int do_cleanup); + +#if defined(IMAGE_FREEBSD) && defined(ELF_IMAGE) +static void elf_freebsd_probe(void); +static void elf_freebsd_fixup_segment(void); +static void elf_freebsd_find_segment_end(void); +static int elf_freebsd_debug_loader(unsigned int offset); +static void elf_freebsd_boot(unsigned long entry); +#else +#define elf_freebsd_probe() do {} while(0) +#define elf_freebsd_fixup_segment() do {} while(0) +#define elf_freebsd_find_segment_end() do {} while(0) +#define elf_freebsd_debug_loader(off) (0) +#define elf_freebsd_boot(entry) do {} while(0) +#endif +#if defined(IMAGE_FREEBSD) && defined(AOUT_IMAGE) +static void aout_freebsd_probe(void); +static void aout_freebsd_boot(void); +#else +#define aout_freebsd_probe() do {} while(0) +#define aout_freebsd_boot() do {} while(0) +#endif + +/************************************************************************** +dead_download - Restart etherboot if probe image fails +**************************************************************************/ +static sector_t dead_download ( unsigned char *data __unused, unsigned int len __unused, int eof __unused) { + longjmp(restart_etherboot, -2); +} + +#ifdef IMAGE_MULTIBOOT +#include "../arch/i386/core/multiboot_loader.c" +#else +#define multiboot_probe(data, len) do {} while(0) +#define multiboot_boot(entry) do {} while(0) +#endif + + +#ifdef WINCE_IMAGE +#include "../arch/i386/core/wince_loader.c" +#endif + +#ifdef AOUT_IMAGE +#include "../arch/i386/core/aout_loader.c" +#endif + +#ifdef TAGGED_IMAGE +#include "../arch/i386/core/tagged_loader.c" +#endif + +#if defined(ELF_IMAGE) || defined(ELF64_IMAGE) +#include "elf_loader.c" +#endif + +#if defined(COFF_IMAGE) +#include "../arch/e1/core/coff_loader.c" +#endif + +#ifdef IMAGE_FREEBSD +#include "../arch/i386/core/freebsd_loader.c" +#endif + +#ifdef PXE_IMAGE +#include "../arch/i386/core/pxe_loader.c" +#endif + +#ifdef RAW_IMAGE +#include "../arch/armnommu/core/raw_loader.c" +#endif + +static void done(int do_cleanup) +{ +#ifdef SIZEINDICATOR + printf("K "); +#endif + printf("done\n"); + /* We may not want to do the cleanup: when booting a PXE + * image, for example, we need to leave the network card + * enabled, and it helps debugging if the serial console + * remains enabled. The call the cleanup() will be triggered + * when the PXE stack is shut down. + */ + if ( do_cleanup ) { + cleanup(); + arch_on_exit(0); + } +} + +static int prep_segment(unsigned long start, unsigned long mid, unsigned long end, + unsigned long istart __unused, unsigned long iend __unused) +{ + unsigned fit, i; + +#if LOAD_DEBUG + printf ( "\nAbout to prepare segment [%lX,%lX)\n", start, end ); + sleep ( 3 ); +#endif + + if (mid > end) { + printf("filesz > memsz\n"); + return 0; + } + if ((end > virt_to_phys(_text)) && + (start < virt_to_phys(_end))) { + printf("segment [%lX, %lX) overlaps etherboot [%lX, %lX)\n", + start, end, + virt_to_phys(_text), virt_to_phys(_end) + ); + return 0; + } + if ((end > heap_ptr) && (start < heap_bot)) { + printf("segment [%lX, %lX) overlaps heap [%lX, %lX)\n", + start, end, + heap_ptr, heap_bot + ); + return 0; + } + fit = 0; + for(i = 0; i < meminfo.map_count; i++) { + unsigned long long r_start, r_end; + if (meminfo.map[i].type != E820_RAM) + continue; + r_start = meminfo.map[i].addr; + r_end = r_start + meminfo.map[i].size; + if ((start >= r_start) && (end <= r_end)) { + fit = 1; + break; + } + } + if (!fit) { + printf("\nsegment [%lX,%lX) does not fit in any memory region\n", + start, end); +#if LOAD_DEBUG + printf("Memory regions(%d):\n", meminfo.map_count); + for(i = 0; i < meminfo.map_count; i++) { + unsigned long long r_start, r_end; + if (meminfo.map[i].type != E820_RAM) + continue; + r_start = meminfo.map[i].addr; + r_end = r_start + meminfo.map[i].size; + printf("[%X%X, %X%X) type %d\n", + (unsigned long)(r_start >> 32), + (unsigned long)r_start, + (unsigned long)(r_end >> 32), + (unsigned long)r_end, + meminfo.map[i].type); + } +#endif + return 0; + } +#if LOAD_DEBUG + /* Zap the whole lot. Do this so that if we're treading on + * anything, it shows up now, when the debug message is + * visible, rather than when we're partway through downloading + * the file. + * + * If you see an entire screen full of exclamation marks, then + * you've almost certainly written all over the display RAM. + * This is likely to happen if the status of the A20 line gets + * screwed up. Of course, if this happens, it's a good bet + * that you've also trashed the whole of low memory, so expect + * interesting things to happen... + */ + memset(phys_to_virt(start), '!', mid - start); +#endif + /* Zero the bss */ + if (end > mid) { + memset(phys_to_virt(mid), 0, end - mid); + } + return 1; +} + +static unsigned long find_segment(unsigned long size, unsigned long align) +{ + unsigned i; + /* Verify I have a power of 2 alignment */ + if (align & (align - 1)) { + return ULONG_MAX; + } + for(i = 0; i < meminfo.map_count; i++) { + unsigned long r_start, r_end; + if (meminfo.map[i].type != E820_RAM) + continue; + if ((meminfo.map[i].addr + meminfo.map[i].size) > ULONG_MAX) { + continue; + } + r_start = meminfo.map[i].addr; + r_end = r_start + meminfo.map[i].size; + /* Don't allow the segment to overlap etherboot */ + if ((r_end > virt_to_phys(_text)) && (r_start < virt_to_phys(_text))) { + r_end = virt_to_phys(_text); + } + if ((r_start > virt_to_phys(_text)) && (r_start < virt_to_phys(_end))) { + r_start = virt_to_phys(_end); + } + /* Don't allow the segment to overlap the heap */ + if ((r_end > heap_ptr) && (r_start < heap_ptr)) { + r_end = heap_ptr; + } + if ((r_start > heap_ptr) && (r_start < heap_bot)) { + r_start = heap_ptr; + } + r_start = (r_start + align - 1) & ~(align - 1); + if ((r_end >= r_start) && ((r_end - r_start) >= size)) { + return r_start; + } + } + /* I did not find anything :( */ + return ULONG_MAX; +} + +/************************************************************************** +PROBE_IMAGE - Detect image file type +**************************************************************************/ +os_download_t probe_image(unsigned char *data, unsigned int len) +{ + os_download_t os_download = 0; +#ifdef AOUT_IMAGE + if (!os_download) os_download = aout_probe(data, len); +#endif +#ifdef ELF_IMAGE + if (!os_download) os_download = elf32_probe(data, len); +#endif +#ifdef ELF64_IMAGE + if (!os_download) os_download = elf64_probe(data, len); +#endif +#ifdef COFF_IMAGE + if (!os_download) os_download = coff_probe(data, len); +#endif +#ifdef WINCE_IMAGE + if (!os_download) os_download = wince_probe(data, len); +#endif +#ifdef TAGGED_IMAGE + if (!os_download) os_download = tagged_probe(data, len); +#endif +/* PXE_IMAGE must always be last */ +#ifdef PXE_IMAGE + if (!os_download) os_download = pxe_probe(data, len); +#endif +#ifdef RAW_IMAGE + if (!os_download) os_download = raw_probe(data, len); +#endif + return os_download; +} + +/************************************************************************** +LOAD_BLOCK - Try to load file +**************************************************************************/ +int load_block(unsigned char *data, unsigned int block, unsigned int len, int eof) +{ + static os_download_t os_download; + static sector_t skip_sectors; + static unsigned int skip_bytes; +#ifdef SIZEINDICATOR + static int rlen = 0; + + if (block == 1) + { + rlen=len; + printf("XXXX"); + } + 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) + { + skip_sectors = 0; + skip_bytes = 0; + os_download = probe_image(data, len); + if (!os_download) { + printf("error: not a valid image\n"); +#if 0 + printf("block: %d len: %d\n", block, len); + printf("%hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx\n", + data[0], data[1], data[2], data[3], + data[4], data[5], data[6], data[7]); +#endif + return 0; + } + } /* end of block zero processing */ + + /* Either len is greater or the skip is greater */ + if ((skip_sectors > (len >> 9)) || + ((skip_sectors == (len >> 9)) && (skip_bytes >= (len & 0x1ff)))) { + /* If I don't have enough bytes borrow them from skip_sectors */ + if (skip_bytes < len) { + skip_sectors -= (len - skip_bytes + 511) >> 9; + skip_bytes += (len - skip_bytes + 511) & ~0x1ff; + } + skip_bytes -= len; + } + else { + len -= (skip_sectors << 9) + skip_bytes; + data += (skip_sectors << 9) + skip_bytes; + } + skip_sectors = os_download(data, len, eof); + skip_bytes = 0; + + return 1; +} + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ + diff --git a/src/core/pc_kbd.c b/src/core/pc_kbd.c new file mode 100644 index 00000000..9fe82924 --- /dev/null +++ b/src/core/pc_kbd.c @@ -0,0 +1,108 @@ +/* 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 + */ +#ifdef CONSOLE_PC_KBD +#include "etherboot.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; +} + +int kbd_havekey(void) +{ + if (!cur_scan) + cur_scan = get_scancode(); + return cur_scan != 0; +} + +int kbd_ischar(void) +{ + if (!kbd_havekey()) + return 0; + if (!key_map[shift_state & SHIFT][cur_scan]) { + cur_scan = 0; + return 0; + } + return 1; +} + +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; +} +#endif diff --git a/src/core/pci.c b/src/core/pci.c new file mode 100644 index 00000000..8f89d8df --- /dev/null +++ b/src/core/pci.c @@ -0,0 +1,337 @@ +#ifdef CONFIG_PCI + +/* + * 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 "pci.h" + +/*#define DEBUG 1*/ + +static void scan_drivers( + int type, + uint32_t class, uint16_t vendor, uint16_t device, + const struct pci_driver *last_driver, struct pci_device *dev) +{ + const struct pci_driver *skip_driver = last_driver; + /* Assume there is only one match of the correct type */ + const struct pci_driver *driver; + + for(driver = pci_drivers; driver < pci_drivers_end; driver++) { + int i; + if (driver->type != type) + continue; + if (skip_driver) { + if (skip_driver == driver) + skip_driver = 0; + continue; + } + for(i = 0; i < driver->id_count; i++) { + if ((vendor == driver->ids[i].vendor) && + (device == driver->ids[i].dev_id)) { + + dev->driver = driver; + dev->name = driver->ids[i].name; + + goto out; + } + } + } + if (!class) { + goto out; + } + for(driver = pci_drivers; driver < pci_drivers_end; driver++) { + if (driver->type != type) + continue; + if (skip_driver) { + if (skip_driver == driver) + skip_driver = 0; + continue; + } + if (last_driver == driver) + continue; + if ((class >> 8) == driver->class) { + dev->driver = driver; + dev->name = driver->name; + goto out; + } + } + out: + return; +} + +void scan_pci_bus(int type, struct pci_device *dev) +{ + unsigned int first_bus, first_devfn; + const struct pci_driver *first_driver; + unsigned int devfn, bus, buses; + unsigned char hdr_type = 0; + uint32_t class; + uint16_t vendor, device; + uint32_t l, membase, ioaddr, romaddr; + uint8_t irq; + int reg; + + first_bus = 0; + first_devfn = 0; + first_driver = 0; + if (dev->driver || dev->use_specified) { + first_driver = dev->driver; + first_bus = dev->bus; + first_devfn = dev->devfn; + /* Re read the header type on a restart */ + pcibios_read_config_byte(first_bus, first_devfn & ~0x7, + PCI_HEADER_TYPE, &hdr_type); + dev->driver = 0; + dev->bus = 0; + dev->devfn = 0; + } + + /* Scan all PCI buses, until we find our card. + * We could be smart only scan the required buses but that + * is error prone, and tricky. + * By scanning all possible pci buses in order we should find + * our card eventually. + */ + buses=256; + for (bus = first_bus; bus < buses; ++bus) { + for (devfn = first_devfn; devfn < 0xff; ++devfn, first_driver = 0) { + if (PCI_FUNC (devfn) == 0) + pcibios_read_config_byte(bus, devfn, PCI_HEADER_TYPE, &hdr_type); + else if (!(hdr_type & 0x80)) /* not a multi-function device */ + continue; + pcibios_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l); + /* some broken boards return 0 if a slot is empty: */ + if (l == 0xffffffff || l == 0x00000000) { + continue; + } + vendor = l & 0xffff; + device = (l >> 16) & 0xffff; + + pcibios_read_config_dword(bus, devfn, PCI_REVISION, &l); + class = (l >> 8) & 0xffffff; +#if DEBUG + { + int i; + printf("%hhx:%hhx.%hhx [%hX/%hX] Class %hX\n", + bus, PCI_SLOT(devfn), PCI_FUNC(devfn), + vendor, device, class >> 8); +#if DEBUG > 1 + for(i = 0; i < 256; i++) { + unsigned char byte; + if ((i & 0xf) == 0) { + printf("%hhx: ", i); + } + pcibios_read_config_byte(bus, devfn, i, &byte); + printf("%hhx ", byte); + if ((i & 0xf) == 0xf) { + printf("\n"); + } + } +#endif + + } +#endif + scan_drivers(type, class, vendor, device, first_driver, dev); + if (!dev->driver) + continue; + + dev->devfn = devfn; + dev->bus = bus; + dev->class = class; + dev->vendor = vendor; + dev->dev_id = device; + + + /* Get the ROM base address */ + pcibios_read_config_dword(bus, devfn, + PCI_ROM_ADDRESS, &romaddr); + romaddr >>= 10; + dev->romaddr = romaddr; + + /* Get the ``membase'' */ + pcibios_read_config_dword(bus, devfn, + PCI_BASE_ADDRESS_1, &membase); + dev->membase = membase; + + /* Get the ``ioaddr'' */ + for (reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4) { + pcibios_read_config_dword(bus, devfn, reg, &ioaddr); + if ((ioaddr & PCI_BASE_ADDRESS_IO_MASK) == 0 || (ioaddr & PCI_BASE_ADDRESS_SPACE_IO) == 0) + continue; + + + /* Strip the I/O address out of the returned value */ + ioaddr &= PCI_BASE_ADDRESS_IO_MASK; + + /* Take the first one or the one that matches in boot ROM address */ + dev->ioaddr = ioaddr; + } + + /* Get the irq */ + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &irq); + if (irq) { + pci_read_config_byte(dev, PCI_INTERRUPT_LINE, + &irq); + } + dev->irq = irq; + +#if DEBUG > 2 + printf("Found %s ROM address %#hx\n", + dev->name, romaddr); +#endif + return; + } + first_devfn = 0; + } + first_bus = 0; +} + + + +/* + * 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 *p) +{ + unsigned short new_command, pci_command; + unsigned char pci_latency; + + pcibios_read_config_word(p->bus, p->devfn, PCI_COMMAND, &pci_command); + new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO; + if (pci_command != new_command) { +#if DEBUG > 0 + printf( + "The PCI BIOS has not enabled this device!\n" + "Updating PCI command %hX->%hX. pci_bus %hhX pci_device_fn %hhX\n", + pci_command, new_command, p->bus, p->devfn); +#endif + pcibios_write_config_word(p->bus, p->devfn, PCI_COMMAND, new_command); + } + pcibios_read_config_byte(p->bus, p->devfn, PCI_LATENCY_TIMER, &pci_latency); + if (pci_latency < 32) { +#if DEBUG > 0 + printf("PCI latency timer (CFLT) is unreasonably low at %d. Setting to 32 clocks.\n", + pci_latency); +#endif + pcibios_write_config_byte(p->bus, p->devfn, PCI_LATENCY_TIMER, 32); + } +} + +/* + * Find the start of a pci resource. + */ +unsigned long pci_bar_start(struct pci_device *dev, unsigned int index) +{ + uint32_t lo, hi; + unsigned long bar; + pci_read_config_dword(dev, index, &lo); + if (lo & PCI_BASE_ADDRESS_SPACE_IO) { + bar = lo & PCI_BASE_ADDRESS_IO_MASK; + } else { + bar = 0; + if ((lo & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == PCI_BASE_ADDRESS_MEM_TYPE_64) { + pci_read_config_dword(dev, index + 4, &hi); + if (hi) { +#if ULONG_MAX > 0xffffffff + bar = hi; + bar <<=32; +#else + printf("Unhandled 64bit BAR\n"); + return -1UL; +#endif + } + } + bar |= lo & PCI_BASE_ADDRESS_MEM_MASK; + } + return bar + pcibios_bus_base(dev->bus); +} + +/* + * Find the size of a pci resource. + */ +unsigned long pci_bar_size(struct pci_device *dev, unsigned int bar) +{ + uint32_t start, size; + /* Save the original bar */ + pci_read_config_dword(dev, bar, &start); + /* Compute which bits can be set */ + pci_write_config_dword(dev, bar, ~0); + pci_read_config_dword(dev, bar, &size); + /* Restore the original size */ + pci_write_config_dword(dev, bar, 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; +} + +/** + * pci_find_capability - query for devices' capabilities + * @dev: PCI device to query + * @cap: capability code + * + * Tell if a device supports a given PCI capability. + * Returns the address of the requested capability structure within the + * device's PCI configuration space or 0 in case the device does not + * support it. Possible values for @cap: + * + * %PCI_CAP_ID_PM Power Management + * + * %PCI_CAP_ID_AGP Accelerated Graphics Port + * + * %PCI_CAP_ID_VPD Vital Product Data + * + * %PCI_CAP_ID_SLOTID Slot Identification + * + * %PCI_CAP_ID_MSI Message Signalled Interrupts + * + * %PCI_CAP_ID_CHSWP CompactPCI HotSwap + */ +int pci_find_capability(struct pci_device *dev, int cap) +{ + uint16_t status; + uint8_t pos, id; + uint8_t hdr_type; + int ttl = 48; + + pci_read_config_word(dev, PCI_STATUS, &status); + if (!(status & PCI_STATUS_CAP_LIST)) + return 0; + pci_read_config_byte(dev, PCI_HEADER_TYPE, &hdr_type); + switch (hdr_type & 0x7F) { + case PCI_HEADER_TYPE_NORMAL: + case PCI_HEADER_TYPE_BRIDGE: + default: + pci_read_config_byte(dev, PCI_CAPABILITY_LIST, &pos); + break; + case PCI_HEADER_TYPE_CARDBUS: + pci_read_config_byte(dev, PCI_CB_CAPABILITY_LIST, &pos); + break; + } + while (ttl-- && pos >= 0x40) { + pos &= ~3; + pci_read_config_byte(dev, pos + PCI_CAP_LIST_ID, &id); +#if DEBUG > 0 + printf("Capability: %d\n", id); +#endif + if (id == 0xff) + break; + if (id == cap) + return pos; + pci_read_config_byte(dev, pos + PCI_CAP_LIST_NEXT, &pos); + } + return 0; +} + +#endif /* CONFIG_PCI */ diff --git a/src/core/pci_probe.c b/src/core/pci_probe.c new file mode 100644 index 00000000..d7d55b2d --- /dev/null +++ b/src/core/pci_probe.c @@ -0,0 +1,70 @@ +#ifdef CONFIG_PCI +/* + * 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 "pci.h" + +void pci_enumerate(void) +{ + const struct pci_driver *driver; + for(driver = pci_drivers; driver < pci_drivers_end; driver++) { + printf("%s ", driver->name); + } +} + +int pci_probe(struct dev *dev, const char *type_name) +{ +/* + * NIC probing is in pci device order, followed by the + * link order of the drivers. A driver that matches + * on vendor and device id will supersede a driver + * that matches on pci class. + * + * If you want to probe for another device behind the same pci + * device just increment index. And the previous probe call + * will be repeated. + */ + struct pci_probe_state *state = &dev->state.pci; + printf("Probing pci %s...\n", type_name); + if (dev->how_probe == PROBE_FIRST) { + state->advance = 1; + state->dev.driver = 0; + state->dev.bus = 0; + state->dev.devfn = 0; + dev->index = -1; + } + for(;;) { + if ((dev->how_probe != PROBE_AWAKE) && state->advance) { + find_pci(dev->type, &state->dev); + dev->index = -1; + } + state->advance = 1; + + if (state->dev.driver == 0) + break; + + if (dev->how_probe != PROBE_AWAKE) { + dev->type_index++; + } + dev->devid.bus_type = PCI_BUS_TYPE; + dev->devid.vendor_id = htons(state->dev.vendor); + dev->devid.device_id = htons(state->dev.dev_id); + /* FIXME how do I handle dev->index + PROBE_AGAIN?? */ + + printf("[%s]", state->dev.name); + if (state->dev.driver->probe(dev, &state->dev)) { + state->advance = (dev->index == -1); + return PROBE_WORKED; + } + putchar('\n'); + } + return PROBE_FAILED; +} + +#endif diff --git a/src/core/pcmcia.c b/src/core/pcmcia.c new file mode 100644 index 00000000..2a7cf2b8 --- /dev/null +++ b/src/core/pcmcia.c @@ -0,0 +1,269 @@ +#ifdef CONFIG_PCMCIA + +/* + * 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 "../include/pcmcia.h" +#include "../include/i82365.h" +#define CODE_STATUS "alpha" +#define CODE_VERSION "0.1.3" + +#include "../include/pcmcia-opts.h" +#include "../include/pcmcia.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; ) { + poll_interruptions(); + } + return; +} + +int 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 ( "\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 ( "\n" ); getchar(); + } + continue; + } + } + } + if ( PDEBUG > 2 ) { + printf ( "\n" ); + getchar(); + } + + return 0; +} + +int pcmcia_shutdown_all(void) { + int i; + //if ( PDEBUG > 2 ) {printf("\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"); + return 0; +} + +#endif /* CONFIG_PCMCIA */ diff --git a/src/core/proto_eth_slow.c b/src/core/proto_eth_slow.c new file mode 100644 index 00000000..d19a43e9 --- /dev/null +++ b/src/core/proto_eth_slow.c @@ -0,0 +1,407 @@ +/* Copyright 2004 Linux Networx */ +#ifdef PROTO_LACP +#if 0 +#include "etherboot.h" +#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/src/core/proto_http.c b/src/core/proto_http.c new file mode 100644 index 00000000..f2dc9dd1 --- /dev/null +++ b/src/core/proto_http.c @@ -0,0 +1,206 @@ +#include "etherboot.h" +#include "http.h" + +#ifdef DOWNLOAD_PROTO_HTTP + +/* The block size is currently chosen to be 512 bytes. This means, we can + allocate the receive buffer on the stack, but it results in a noticeable + performance penalty. + This is what needs to be done in order to increase the block size: + - size negotiation needs to be implemented in TCP + - the buffer needs to be allocated on the heap + - path MTU discovery needs to be implemented +*/ /***/ /* FIXME */ +#define BLOCKSIZE TFTP_DEFAULTSIZE_PACKET + +/************************************************************************** +SEND_TCP_CALLBACK - Send data using TCP +**************************************************************************/ +struct send_recv_state { + int (*fnc)(unsigned char *data, int block, int len, int eof); + char *send_buffer; + char *recv_buffer; + int send_length; + int recv_length; + int bytes_sent; + int block; + int bytes_received; + enum { RESULT_CODE, HEADER, DATA, ERROR, MOVED } recv_state; + int rc; + char location[MAX_URL+1]; +}; + +static int send_tcp_request(int length, void *buffer, void *ptr) { + struct send_recv_state *state = (struct send_recv_state *)ptr; + + if (length > state->send_length - state->bytes_sent) + length = state->send_length - state->bytes_sent; + memcpy(buffer, state->send_buffer + state->bytes_sent, length); + state->bytes_sent += length; + return (length); +} + +/************************************************************************** +RECV_TCP_CALLBACK - Receive data using TCP +**************************************************************************/ +static int recv_tcp_request(int length, const void *buffer, void *ptr) { + struct send_recv_state *state = (struct send_recv_state *)ptr; + + /* Assume that the lines in an HTTP header do not straddle a packet */ + /* boundary. This is probably a reasonable assumption */ + if (state->recv_state == RESULT_CODE) { + while (length > 0) { + /* Find HTTP result code */ + if (*(const char *)buffer == ' ') { + const char *ptr = ((const char *)buffer) + 1; + int rc = strtoul(ptr, &ptr, 10); + if (ptr >= (const char *)buffer + length) { + state->recv_state = ERROR; + return 0; + } + state->rc = rc; + state->recv_state = HEADER; + goto header; + } + ++(const char *)buffer; + length--; + } + state->recv_state = ERROR; + return 0; + } + if (state->recv_state == HEADER) { + header: while (length > 0) { + /* Check for HTTP redirect */ + if (state->rc >= 300 && state->rc < 400 && + !memcmp(buffer, "Location: ", 10)) { + char *ptr = state->location; + int i; + memcpy(ptr, buffer + 10, MAX_URL); + for (i = 0; i < MAX_URL && *ptr > ' '; + i++, ptr++); + *ptr = '\000'; + state->recv_state = MOVED; + return 1; + } + /* Find beginning of line */ + while (length > 0) { + length--; + if (*((const char *)buffer)++ == '\n') + break; + } + /* Check for end of header */ + if (length >= 2 && !memcmp(buffer, "\r\n", 2)) { + state->recv_state = DATA; + buffer += 2; + length -= 2; + break; + } + } + } + if (state->recv_state == DATA) { + state->bytes_received += length; + while (length > 0) { + int copy_length = BLOCKSIZE - state->recv_length; + if (copy_length > length) + copy_length = length; + memcpy(state->recv_buffer + state->recv_length, + buffer, copy_length); + if ((state->recv_length += copy_length) == BLOCKSIZE) { + if (!state->fnc(state->recv_buffer, + ++state->block, BLOCKSIZE, 0)) + return 0; + state->recv_length = 0; + } + length -= copy_length; + buffer += copy_length; + } + } + return 1; +} + +/************************************************************************** +HTTP_GET - Get data using HTTP +**************************************************************************/ +int http(const char *url, + int (*fnc)(unsigned char *, unsigned int, unsigned int, int)) { + static const char GET[] = "GET /%s HTTP/1.0\r\n\r\n"; + static char recv_buffer[BLOCKSIZE]; + in_addr destip; + int port; + int length; + struct send_recv_state state; + + state.fnc = fnc; + state.rc = -1; + state.block = 0; + state.recv_buffer = recv_buffer; + length = strlen(url); + if (length <= MAX_URL) { + memcpy(state.location, url, length+1); + destip = arptable[ARP_SERVER].ipaddr; + port = url_port; + if (port == -1) + port = 80; + goto first_time; + + do { + state.rc = -1; + state.block = 0; + url = state.location; + if (memcmp("http://", url, 7)) + break; + url += 7; + length = inet_aton(url, &destip); + if (!length) { + /* As we do not have support for DNS, assume*/ + /* that HTTP redirects always point to the */ + /* same machine */ + if (state.recv_state == MOVED) { + while (*url && + *url != ':' && *url != '/') url++; + } else { + break; + } + } + if (*(url += length) == ':') { + port = strtoul(url, &url, 10); + } else { + port = 80; + } + if (!*url) + url = "/"; + if (*url != '/') + break; + url++; + + first_time: + length = strlen(url); + state.send_length = sizeof(GET) - 3 + length; + + { char buf[state.send_length + 1]; + sprintf(state.send_buffer = buf, GET, url); + state.bytes_sent = 0; + + state.bytes_received = 0; + state.recv_state = RESULT_CODE; + + state.recv_length = 0; + tcp_transaction(destip.s_addr, 80, &state, + send_tcp_request, recv_tcp_request); + } + } while (state.recv_state == MOVED); + } else { + memcpy(state.location, url, MAX_URL); + state.location[MAX_URL] = '\000'; + } + + if (state.rc == 200) { + return fnc(recv_buffer, ++state.block, state.recv_length, 1); + } else { + printf("Failed to download %s (rc = %d)\n", + state.location, state.rc); + return 0; + } +} + +#endif /* DOWNLOAD_PROTO_HTTP */ diff --git a/src/core/proto_slam.c b/src/core/proto_slam.c new file mode 100644 index 00000000..135384a9 --- /dev/null +++ b/src/core/proto_slam.c @@ -0,0 +1,541 @@ +#ifdef DOWNLOAD_PROTO_SLAM +#include "etherboot.h" +#include "nic.h" + +#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; + + 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 { + in_addr server_ip; + in_addr multicast_ip; + in_addr local_ip; + uint16_t server_port; + uint16_t multicast_port; + uint16_t local_port; + int (*fnc)(unsigned char *, unsigned int, unsigned int, int); + int sent_nack; +}; + +#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 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_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_ip.s_addr) && + (ntohs(udp->dest) == info->multicast_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; + } + if (state.bitmap) { + forget(state.bitmap); + } + bitmap_len = (state.total_packets + 1 + 7)/8; + state.bitmap = allot(bitmap_len); + state.image = allot(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("\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_ip.s_addr, + info->local_port, info->server_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(); + + retry = -1; + rx_qdrain(); + /* Arp for my server */ + if (arptable[ARP_SERVER].ipaddr.s_addr != info->server_ip.s_addr) { + arptable[ARP_SERVER].ipaddr.s_addr = info->server_ip.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_ip.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 info->fnc(state.image, 1, state.total_bytes, 1); +} + + +int url_slam(const char *name, int (*fnc)(unsigned char *, unsigned int, unsigned int, int)) +{ + struct slam_info info; + /* Set the defaults */ + info.server_ip.s_addr = arptable[ARP_SERVER].ipaddr.s_addr; + info.server_port = SLAM_PORT; + info.multicast_ip.s_addr = htonl(SLAM_MULTICAST_IP); + info.multicast_port = SLAM_MULTICAST_PORT; + info.local_ip.s_addr = arptable[ARP_CLIENT].ipaddr.s_addr; + info.local_port = SLAM_LOCAL_PORT; + info.fnc = fnc; + info.sent_nack = 0; + /* Now parse the url */ + if (url_port != -1) { + info.server_port = url_port; + } + if (name[0]) { + /* multicast ip */ + name += inet_aton(name, &info.multicast_ip); + if (name[0] == ':') { + name++; + info.multicast_port = strtoul(name, &name, 10); + } + } + if (name[0]) { + printf("\nBad url\n"); + return 0; + } + return proto_slam(&info); +} + +#endif /* DOWNLOAD_PROTO_SLAM */ diff --git a/src/core/proto_tftm.c b/src/core/proto_tftm.c new file mode 100644 index 00000000..8040fc44 --- /dev/null +++ b/src/core/proto_tftm.c @@ -0,0 +1,491 @@ +/************************************************************************** +* +* proto_tftm.c -- Etherboot Multicast TFTP +* Written 2003-2003 by Timothy Legge +* +* 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 code is based on the DOWNLOAD_PROTO_TFTM section of +* Etherboot 5.3 core/nic.c and: +* +* Anselm Martin Hoffmeister's previous proto_tftm.c multicast work +* Eric Biederman's proto_slam.c +* +* $Revision$ +* $Author$ +* $Date$ +* +* REVISION HISTORY: +* ================ +* 09-07-2003 timlegge Release Version, Capable of Multicast Booting +* 08-30-2003 timlegge Initial version, Assumes consecutive blocks +* +* Indent Options: indent -kr -i8 +***************************************************************************/ + +#ifdef DOWNLOAD_PROTO_TFTM +#include "etherboot.h" +#include "nic.h" + +//#define TFTM_DEBUG +#ifdef TFTM_DEBUG +#define debug(x) printf x +#else +#define debug(x) +#endif +struct tftm_info { + in_addr server_ip; + in_addr multicast_ip; + in_addr local_ip; + uint16_t server_port; + uint16_t multicast_port; + uint16_t local_port; + int (*fnc) (unsigned char *, unsigned int, unsigned int, int); + int sent_nack; + const char *name; /* Filename */ +}; + +struct tftm_state { + unsigned long block_size; + unsigned long total_bytes; + unsigned long total_packets; + char ismaster; + unsigned long received_packets; + unsigned char *image; + unsigned char *bitmap; + char recvd_oack; +} state; + +#define TFTM_PORT 1758 +#define TFTM_MIN_PACKET 1024 + + +int opt_get_multicast(struct tftp_t *tr, unsigned short *len, + unsigned long *filesize, struct tftm_info *info); + +static int await_tftm(int ival, void *ptr, unsigned short ptype __unused, + struct iphdr *ip, struct udphdr *udp) +{ + struct tftm_info *info = ptr; + + /* Check for Unicast data being received */ + if (ip->dest.s_addr == arptable[ARP_CLIENT].ipaddr.s_addr) { + if (!udp) { + return 0; + } + if (arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr) + return 0; + if (ntohs(udp->dest) != ival) + return 0; + + return 1; /* Unicast Data Received */ + } + + /* Also check for Multicast data being received */ + if ((ip->dest.s_addr == info->multicast_ip.s_addr) && + (ntohs(udp->dest) == info->multicast_port) && + (nic.packetlen >= ETH_HLEN + sizeof(struct iphdr) + + sizeof(struct udphdr))) { + return 1; /* Multicast data received */ + } + return 0; +} + +int proto_tftm(struct tftm_info *info) +{ + int retry = 0; + static unsigned short iport = 2000; + unsigned short oport = 0; + unsigned short len, block = 0, prevblock = 0; + struct tftp_t *tr; + struct tftpreq_t tp; + unsigned long filesize = 0; + + state.image = 0; + state.bitmap = 0; + + rx_qdrain(); + + /* Warning: the following assumes the layout of bootp_t. + But that's fixed by the IP, UDP and BOOTP specs. */ + + /* Send a tftm-request to the server */ + tp.opcode = htons(TFTP_RRQ); /* Const for "\0x0" "\0x1" =^= ReadReQuest */ + len = + sizeof(tp.ip) + sizeof(tp.udp) + sizeof(tp.opcode) + + sprintf((char *) tp.u.rrq, + "%s%coctet%cmulticast%c%cblksize%c%d%ctsize%c", + info->name, 0, 0, 0, 0, 0, TFTM_MIN_PACKET, 0, 0) + 1; + + if (!udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, ++iport, + TFTM_PORT, len, &tp)) + return (0); + + /* loop to listen for packets and to receive the file */ + for (;;) { + long timeout; +#ifdef CONGESTED + timeout = + rfc2131_sleep_interval(block ? TFTP_REXMT : TIMEOUT, + retry); +#else + timeout = rfc2131_sleep_interval(TIMEOUT, retry); +#endif + /* Calls the await_reply function in nic.c which in turn calls + await_tftm (1st parameter) as above */ + if (!await_reply(await_tftm, iport, info, timeout)) { + if (!block && retry++ < MAX_TFTP_RETRIES) { /* maybe initial request was lost */ + if (!udp_transmit + (arptable[ARP_SERVER].ipaddr.s_addr, + ++iport, TFTM_PORT, len, &tp)) + return (0); + continue; + } +#ifdef CONGESTED + if (block && ((retry += TFTP_REXMT) < TFTP_TIMEOUT)) { /* we resend our last ack */ +#ifdef MDEBUG + printf("\n"); +#endif + debug(("Timed out receiving file")); + len = + sizeof(tp.ip) + sizeof(tp.udp) + + sizeof(tp.opcode) + + sprintf((char *) tp.u.rrq, + "%s%coctet%cmulticast%c%cblksize%c%d%ctsize%c", + info->name, 0, 0, 0, 0, 0, + TFTM_MIN_PACKET, 0, 0) + 1; + + udp_transmit + (arptable[ARP_SERVER].ipaddr.s_addr, + ++iport, TFTM_PORT, len, &tp); + continue; + } +#endif + break; /* timeout */ + } + + tr = (struct tftp_t *) &nic.packet[ETH_HLEN]; + + 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)) { + int i = + opt_get_multicast(tr, &len, &filesize, info); + + if (i == 0 || (i != 7 && !state.recvd_oack)) { /* Multicast unsupported */ + /* Transmit an error message to the server to end the transmission */ + printf + ("TFTM-Server doesn't understand options [blksize tsize multicast]\n"); + tp.opcode = htons(TFTP_ERROR); + tp.u.err.errcode = 8; + /* + * Warning: the following assumes the layout of bootp_t. + * But that's fixed by the IP, UDP and BOOTP specs. + */ + len = + sizeof(tp.ip) + sizeof(tp.udp) + + sizeof(tp.opcode) + + sizeof(tp.u.err.errcode) + + /* + * Normally bad form to omit the format string, but in this case + * the string we are copying from is fixed. sprintf is just being + * used as a strcpy and strlen. + */ + sprintf((char *) tp.u.err.errmsg, + "RFC2090 error") + 1; + udp_transmit(arptable[ARP_SERVER].ipaddr. + s_addr, iport, + ntohs(tr->udp.src), len, &tp); + block = tp.u.ack.block = 0; /* this ensures, that */ + /* the packet does not get */ + /* processed as data! */ + return (0); + } else { + unsigned long bitmap_len; + /* */ + if (!state.recvd_oack) { + + state.total_packets = + 1 + (filesize - + (filesize % + state.block_size)) / + state.block_size; + bitmap_len = + (state.total_packets + 7) / 8; + if (!state.image) { + state.bitmap = + allot(bitmap_len); + state.image = + allot(filesize); + + if ((unsigned long) state. + image < 1024 * 1024) { + printf + ("ALERT: tftp filesize to large for available memory\n"); + return 0; + } + memset(state.bitmap, 0, + bitmap_len); + } + /* If I'm running over multicast join the multicast group */ + join_group(IGMP_SERVER, + info->multicast_ip. + s_addr); + } + state.recvd_oack = 1; + } + + + + } else if (tr->opcode == htons(TFTP_DATA)) { + unsigned long data_len; + unsigned char *data; + struct udphdr *udp; + udp = + (struct udphdr *) &nic.packet[ETH_HLEN + + sizeof(struct + iphdr)]; + len = + ntohs(tr->udp.len) - sizeof(struct udphdr) - 4; + data = + nic.packet + ETH_HLEN + sizeof(struct iphdr) + + sizeof(struct udphdr) + 4; + + if (len > TFTM_MIN_PACKET) /* shouldn't happen */ + continue; /* ignore it */ + + block = ntohs(tp.u.ack.block = tr->u.data.block); + + if (block > state.total_packets) { + printf("ALERT: Invalid packet number\n"); + continue; + } + + /* Compute the expected data length */ + if (block != state.total_packets) { + data_len = state.block_size; + } else { + data_len = filesize % 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: %d\n", + block); + continue; + } + if (nic.packetlen < data_len + (data - nic.packet)) { + printf + ("ALERT: Ethernet packet shorter than data_len: %d\n", + block); + continue; + } + + if (data_len > state.block_size) { + data_len = state.block_size; + } + if (((state. + bitmap[block >> 3] >> (block & 7)) & 1) == + 0) { + /* Non duplicate packet */ + state.bitmap[block >> 3] |= + (1 << (block & 7)); + memcpy(state.image + + ((block - 1) * state.block_size), + data, data_len); + state.received_packets++; + } else { + +/* printf("\n"); */ + } + } + + else { /* neither TFTP_OACK, TFTP_DATA nor TFTP_ERROR */ + break; + } + + if (state.received_packets <= state.total_packets) { + unsigned long b; + unsigned long len; + unsigned long max; + int value; + int last; + + /* Compute the last bit and store an inverted trailer */ + max = state.total_packets + 1; + 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 = 0; /* Start with the received packets */ + for (b = 1; b <= max; b++) { + value = + (state.bitmap[b >> 3] >> (b & 7)) & 1; + + if (value == 0) { + tp.u.ack.block = htons(b - 1); /* Acknowledge the previous block */ + break; + } + } + } + if (state.ismaster) { + 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 (state.received_packets == state.total_packets) { + /* If the client is finished and not the master, + * ack the last packet */ + if (!state.ismaster) { + tp.opcode = htons(TFTP_ACK); + /* Ack Last packet to end xfer */ + tp.u.ack.block = htons(state.total_packets); + oport = ntohs(tr->udp.src); + udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, iport, oport, TFTP_MIN_PACKET, &tp); /* ack */ + } + /* We are done get out */ + forget(state.bitmap); + break; + } + + 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? */ + + } + /* Leave the multicast group */ + leave_group(IGMP_SERVER); + return info->fnc(state.image, 1, filesize, 1); +} + +int url_tftm(const char *name, + int (*fnc) (unsigned char *, unsigned int, unsigned int, int)) +{ + + int ret; + struct tftm_info info; + + /* Set the defaults */ + info.server_ip.s_addr = arptable[ARP_SERVER].ipaddr.s_addr; + info.server_port = TFTM_PORT; + info.local_ip.s_addr = arptable[ARP_CLIENT].ipaddr.s_addr; + info.local_port = TFTM_PORT; /* Does not matter. So take tftm port too. */ + info.multicast_ip.s_addr = info.local_ip.s_addr; + info.multicast_port = TFTM_PORT; + info.fnc = fnc; + state.ismaster = 0; + info.name = name; + + state.block_size = 0; + state.total_bytes = 0; + state.total_packets = 0; + state.received_packets = 0; + state.image = 0; + state.bitmap = 0; + state.recvd_oack = 0; + + if (name[0] != '/') { + /* server ip given, so use it */ + name += inet_aton(info.name, &info.server_ip); + /* No way to specify another port for now */ + } + if (name[0] != '/') { + printf("Bad tftm-URI: [%s]\n", info.name); + return 0; + } + + ret = proto_tftm(&info); + + return ret; +} + +/****************************** +* Parse the multicast options +*******************************/ +int opt_get_multicast(struct tftp_t *tr, unsigned short *len, + unsigned long *filesize, struct tftm_info *info) +{ + const char *p = tr->u.oack.data, *e = 0; + int i = 0; + *len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 2; + if (*len > TFTM_MIN_PACKET) + return -1; + e = p + *len; + + while (*p != '\0' && p < e) { + if (!strcasecmp("tsize", p)) { + p += 6; + if ((*filesize = strtoul(p, &p, 10)) > 0) + i |= 4; + debug(("\n")); + debug(("tsize=%d\n", *filesize)); + while (p < e && *p) + p++; + if (p < e) + p++; + } else if (!strcasecmp("blksize", p)) { + i |= 2; + p += 8; + state.block_size = strtoul(p, &p, 10); + if (state.block_size != TFTM_MIN_PACKET) { + printf + ("TFTM-Server rejected required transfer blocksize %d\n", + TFTM_MIN_PACKET); + return 0; + } + debug(("blksize=%d\n", state.block_size)); + while (p < e && *p) + p++; + if (p < e) + p++; + } else if (!strncmp(p, "multicast", 10)) { + i |= 1; + p += 10; + debug(("multicast options: %s\n", p)); + p += 1 + inet_aton(p, &info->multicast_ip); + debug(("multicast ip = %@\n", info->multicast_ip)); + info->multicast_port = strtoul(p, &p, 10); + ++p; + debug(("multicast port = %d\n", + info->multicast_port)); + state.ismaster = (*p == '1' ? 1 : 0); + debug(("multicast ismaster = %d\n", + state.ismaster)); + while (p < e && *p) + p++; + if (p < e) + p++; + } + } + if (p > e) + return 0; + return i; +} +#endif /* DOWNLOAD_PROTO_TFTP */ diff --git a/src/core/pxe_export.c b/src/core/pxe_export.c new file mode 100644 index 00000000..0199de28 --- /dev/null +++ b/src/core/pxe_export.c @@ -0,0 +1,1424 @@ +/* PXE API interface for Etherboot. + * + * Copyright (C) 2004 Michael Brown . + * + * 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. + */ + +/* Tags used in this file: + * + * FIXME : obvious + * PXESPEC : question over interpretation of the PXE spec. + */ + +#ifdef PXE_EXPORT + +#include "etherboot.h" +#include "pxe.h" +#include "pxe_export.h" +#include "pxe_callbacks.h" +#include "nic.h" +#include "pci.h" +#include "dev.h" +#include "cpu.h" +#include "timer.h" + +#if TRACE_PXE +#define DBG(...) printf ( __VA_ARGS__ ) +#else +#define DBG(...) +#endif + +/* Not sure why this isn't a globally-used structure within Etherboot. + * (Because I didn't want to use packed to prevent gcc from aligning + * source however it liked. Also nstype is a u16, not a uint. - Ken) + */ +typedef struct { + char dest[ETH_ALEN]; + char source[ETH_ALEN]; + unsigned int nstype; +} media_header_t; +static const char broadcast_mac[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF }; + +/* Global pointer to currently installed PXE stack */ +pxe_stack_t *pxe_stack = NULL; + +/* Various startup/shutdown routines. The startup/shutdown call + * sequence is incredibly badly defined in the Intel PXE spec, for + * example: + * + * PXENV_UNDI_INITIALIZE says that the parameters used to initialize + * the adaptor should be those supplied to the most recent + * PXENV_UNDI_STARTUP call. PXENV_UNDI_STARTUP takes no parameters. + * + * PXENV_UNDI_CLEANUP says that the rest of the API will not be + * available after making this call. Figure 3-3 ("Early UNDI API + * usage") shows a call to PXENV_UNDI_CLEANUP being followed by a + * call to the supposedly now unavailable PXENV_STOP_UNDI. + * + * PXENV_UNLOAD_BASE_STACK talks about freeing up the memory + * occupied by the PXE stack. Figure 4-3 ("PXE IPL") shows a call + * to PXENV_STOP_UNDI being made after the call to + * PXENV_UNLOAD_BASE_STACK, by which time the entire PXE stack + * should have been freed (and, potentially, zeroed). + * + * Nothing, anywhere, seems to mention who's responsible for freeing + * up the base memory allocated for the stack segment. It's not + * even clear whether or not this is expected to be in free base + * memory rather than claimed base memory. + * + * Consequently, we adopt a rather defensive strategy, designed to + * work with any conceivable sequence of initialisation or shutdown + * calls. We have only two things that we care about: + * + * 1. Have we hooked INT 1A and INT 15,E820(etc.)? + * 2. Is the NIC initialised? + * + * The NIC should never be initialised without the vectors being + * hooked, similarly the vectors should never be unhooked with the NIC + * still initialised. We do, however, want to be able to have the + * vectors hooked with the NIC shutdown. We therefore have three + * possible states: + * + * 1. Ready to unload: interrupts unhooked, NIC shutdown. + * 2. Midway: interrupts hooked, NIC shutdown. + * 3. Fully ready: interrupts hooked, NIC initialised. + * + * We provide the three states CAN_UNLOAD, MIDWAY and READY to define + * these, and the call pxe_ensure_state() to ensure that the stack is + * in the specified state. All our PXE API call implementations + * should use this call to ensure that the state is as required for + * that PXE API call. This enables us to cope with whatever the + * end-user's interpretation of the PXE spec may be. It even allows + * for someone calling e.g. PXENV_START_UNDI followed by + * PXENV_UDP_WRITE, without bothering with any of the intervening + * calls. + * + * pxe_ensure_state() returns 1 for success, 0 for failure. In the + * event of failure (which can arise from e.g. asking for state READY + * when we don't know where our NIC is), the error code + * PXENV_STATUS_UNDI_INVALID_STATE should be returned to the user. + * The macros ENSURE_XXX() can be used to achieve this without lots of + * duplicated code. + */ + +/* pxe_[un]hook_stack are architecture-specific and provided in + * pxe_callbacks.c + */ + +int pxe_initialise_nic ( void ) { + if ( pxe_stack->state >= READY ) return 1; + + /* Check if NIC is initialised. nic.dev.disable is set to 0 + * when disable() is called, so we use this. + */ + if ( nic.dev.disable ) { + /* NIC may have been initialised independently + * (e.g. when we set up the stack prior to calling the + * NBP). + */ + pxe_stack->state = READY; + return 1; + } + + /* If we already have a NIC defined, reuse that one with + * PROBE_AWAKE. If one was specifed via PXENV_START_UNDI, try + * that one first. Otherwise, set PROBE_FIRST. + */ + if ( nic.dev.state.pci.dev.use_specified == 1 ) { + nic.dev.how_probe = PROBE_NEXT; + DBG ( " initialising NIC specified via START_UNDI" ); + } else if ( nic.dev.state.pci.dev.driver ) { + DBG ( " reinitialising NIC" ); + nic.dev.how_probe = PROBE_AWAKE; + } else { + DBG ( " probing for any NIC" ); + nic.dev.how_probe = PROBE_FIRST; + } + + /* Call probe routine to bring up the NIC */ + if ( eth_probe ( &nic.dev ) != PROBE_WORKED ) { + DBG ( " failed" ); + return 0; + } + pxe_stack->state = READY; + return 1; +} + +int pxe_shutdown_nic ( void ) { + if ( pxe_stack->state <= MIDWAY ) return 1; + + eth_irq ( DISABLE ); + eth_disable(); + pxe_stack->state = MIDWAY; + return 1; +} + +int ensure_pxe_state ( pxe_stack_state_t wanted ) { + int success = 1; + + if ( ! pxe_stack ) return 0; + if ( wanted >= MIDWAY ) + success = success & hook_pxe_stack(); + if ( wanted > MIDWAY ) { + success = success & pxe_initialise_nic(); + } else { + success = success & pxe_shutdown_nic(); + } + if ( wanted < MIDWAY ) + success = success & unhook_pxe_stack(); + return success; +} + +#define ENSURE_CAN_UNLOAD(structure) if ( ! ensure_pxe_state(CAN_UNLOAD) ) { \ + structure->Status = PXENV_STATUS_UNDI_INVALID_STATE; \ + return PXENV_EXIT_FAILURE; } +#define ENSURE_MIDWAY(structure) if ( ! ensure_pxe_state(MIDWAY) ) { \ + structure->Status = PXENV_STATUS_UNDI_INVALID_STATE; \ + return PXENV_EXIT_FAILURE; } +#define ENSURE_READY(structure) if ( ! ensure_pxe_state(READY) ) { \ + structure->Status = PXENV_STATUS_UNDI_INVALID_STATE; \ + return PXENV_EXIT_FAILURE; } + +/***************************************************************************** + * + * Actual PXE API calls + * + *****************************************************************************/ + +/* PXENV_START_UNDI + * + * Status: working + */ +PXENV_EXIT_t pxenv_start_undi ( t_PXENV_START_UNDI *start_undi ) { + unsigned char bus, devfn; + struct pci_probe_state *pci = &nic.dev.state.pci; + struct dev *dev = &nic.dev; + + DBG ( "PXENV_START_UNDI" ); + ENSURE_MIDWAY(start_undi); + + /* Record PCI bus & devfn passed by caller, so we know which + * NIC they want to use. + * + * If they don't match our already-existing NIC structure, set + * values to ensure that the specified NIC is used at the next + * call to pxe_intialise_nic(). + */ + bus = ( start_undi->ax >> 8 ) & 0xff; + devfn = start_undi->ax & 0xff; + + if ( ( pci->dev.driver == NULL ) || + ( pci->dev.bus != bus ) || ( pci->dev.devfn != devfn ) ) { + /* This is quite a bit of a hack and relies on + * knowledge of the internal operation of Etherboot's + * probe mechanism. + */ + DBG ( " set PCI %hhx:%hhx.%hhx", + bus, PCI_SLOT(devfn), PCI_FUNC(devfn) ); + dev->type = BOOT_NIC; + dev->to_probe = PROBE_PCI; + memset ( &dev->state, 0, sizeof(dev->state) ); + pci->advance = 1; + pci->dev.use_specified = 1; + pci->dev.bus = bus; + pci->dev.devfn = devfn; + } + + start_undi->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_UNDI_STARTUP + * + * Status: working + */ +PXENV_EXIT_t pxenv_undi_startup ( t_PXENV_UNDI_STARTUP *undi_startup ) { + DBG ( "PXENV_UNDI_STARTUP" ); + ENSURE_MIDWAY(undi_startup); + + undi_startup->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_UNDI_CLEANUP + * + * Status: working + */ +PXENV_EXIT_t pxenv_undi_cleanup ( t_PXENV_UNDI_CLEANUP *undi_cleanup ) { + DBG ( "PXENV_UNDI_CLEANUP" ); + ENSURE_CAN_UNLOAD ( undi_cleanup ); + + undi_cleanup->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_UNDI_INITIALIZE + * + * Status: working + */ +PXENV_EXIT_t pxenv_undi_initialize ( t_PXENV_UNDI_INITIALIZE + *undi_initialize ) { + DBG ( "PXENV_UNDI_INITIALIZE" ); + ENSURE_MIDWAY ( 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 ( t_PXENV_UNDI_RESET_ADAPTER + *undi_reset_adapter ) { + DBG ( "PXENV_UNDI_RESET_ADAPTER" ); + + ENSURE_MIDWAY ( undi_reset_adapter ); + ENSURE_READY ( undi_reset_adapter ); + + undi_reset_adapter->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_UNDI_SHUTDOWN + * + * Status: working + */ +PXENV_EXIT_t pxenv_undi_shutdown ( t_PXENV_UNDI_SHUTDOWN *undi_shutdown ) { + DBG ( "PXENV_UNDI_SHUTDOWN" ); + ENSURE_MIDWAY ( undi_shutdown ); + + undi_shutdown->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_UNDI_OPEN + * + * Status: working + */ +PXENV_EXIT_t pxenv_undi_open ( t_PXENV_UNDI_OPEN *undi_open ) { + DBG ( "PXENV_UNDI_OPEN" ); + ENSURE_READY ( undi_open ); + + /* PXESPEC: This is where we choose to enable interrupts. + * Can't actually find where we're meant to in the PXE spec, + * but this should work. + */ + eth_irq ( ENABLE ); + + undi_open->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_UNDI_CLOSE + * + * Status: working + */ +PXENV_EXIT_t pxenv_undi_close ( t_PXENV_UNDI_CLOSE *undi_close ) { + DBG ( "PXENV_UNDI_CLOSE" ); + ENSURE_MIDWAY ( undi_close ); + + undi_close->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_UNDI_TRANSMIT + * + * Status: working + */ +PXENV_EXIT_t pxenv_undi_transmit ( t_PXENV_UNDI_TRANSMIT *undi_transmit ) { + t_PXENV_UNDI_TBD *tbd; + const char *dest; + unsigned int type; + unsigned int length; + const char *data; + media_header_t *media_header; + + DBG ( "PXENV_UNDI_TRANSMIT" ); + ENSURE_READY ( undi_transmit ); + + /* We support only the "immediate" portion of the TBD. Who + * knows what Intel's "engineers" were smoking when they came + * up with the array of transmit data blocks... + */ + tbd = SEGOFF16_TO_PTR ( undi_transmit->TBD ); + if ( tbd->DataBlkCount > 0 ) { + undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER; + return PXENV_EXIT_FAILURE; + } + data = SEGOFF16_TO_PTR ( tbd->Xmit ); + length = tbd->ImmedLength; + + /* If destination is broadcast, we need to supply the MAC address */ + if ( undi_transmit->XmitFlag == XMT_BROADCAST ) { + dest = broadcast_mac; + } else { + dest = SEGOFF16_TO_PTR ( undi_transmit->DestAddr ); + } + + /* We can't properly support P_UNKNOWN without rewriting all + * the driver transmit() methods, so we cheat: if P_UNKNOWN is + * specified we rip the destination address and type out of + * the pre-assembled packet, then skip over the header. + */ + switch ( undi_transmit->Protocol ) { + case P_IP: type = IP; break; + case P_ARP: type = ARP; break; + case P_RARP: type = RARP; break; + case P_UNKNOWN: + media_header = (media_header_t*)data; + dest = media_header->dest; + type = ntohs ( media_header->nstype ); + data += ETH_HLEN; + length -= ETH_HLEN; + break; + default: + undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER; + return PXENV_EXIT_FAILURE; + } + + /* Send the packet */ + eth_transmit ( dest, type, length, data ); + + undi_transmit->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_UNDI_SET_MCAST_ADDRESS + * + * Status: stub (no PXE multicast support) + */ +PXENV_EXIT_t pxenv_undi_set_mcast_address ( t_PXENV_UNDI_SET_MCAST_ADDRESS + *undi_set_mcast_address ) { + DBG ( "PXENV_UNDI_SET_MCAST_ADDRESS" ); + /* ENSURE_READY ( undi_set_mcast_address ); */ + undi_set_mcast_address->Status = PXENV_STATUS_UNSUPPORTED; + return PXENV_EXIT_FAILURE; +} + +/* PXENV_UNDI_SET_STATION_ADDRESS + * + * Status: working (deliberately incomplete) + */ +PXENV_EXIT_t pxenv_undi_set_station_address ( t_PXENV_UNDI_SET_STATION_ADDRESS + *undi_set_station_address ) { + DBG ( "PXENV_UNDI_SET_STATION_ADDRESS" ); + ENSURE_READY ( undi_set_station_address ); + + /* We don't offer a facility to set the MAC address; this + * would require adding extra code to all the Etherboot + * drivers, for very little benefit. If we're setting it to + * the current value anyway then return success, otherwise + * return UNSUPPORTED. + */ + if ( memcmp ( nic.node_addr, + &undi_set_station_address->StationAddress, + ETH_ALEN ) == 0 ) { + undi_set_station_address->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; + } + undi_set_station_address->Status = PXENV_STATUS_UNSUPPORTED; + return PXENV_EXIT_FAILURE; +} + +/* 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 ( t_PXENV_UNDI_SET_PACKET_FILTER + *undi_set_packet_filter ) { + DBG ( "PXENV_UNDI_SET_PACKET_FILTER" ); + /* ENSURE_READY ( 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 ( t_PXENV_UNDI_GET_INFORMATION + *undi_get_information ) { + DBG ( "PXENV_UNDI_GET_INFORMATION" ); + ENSURE_READY ( undi_get_information ); + + undi_get_information->BaseIo = nic.ioaddr; + undi_get_information->IntNumber = nic.irqno; + /* Cheat: assume all cards can cope with this */ + undi_get_information->MaxTranUnit = ETH_MAX_MTU; + /* Cheat: we only ever have Ethernet cards */ + undi_get_information->HwType = ETHER_TYPE; + undi_get_information->HwAddrLen = ETH_ALEN; + /* 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, nic.node_addr, + ETH_ALEN ); + memcpy ( &undi_get_information->PermNodeAddress, nic.node_addr, + ETH_ALEN ); + undi_get_information->ROMAddress = 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: won't implement (would require driver API changes for no + * real benefit) + */ +PXENV_EXIT_t pxenv_undi_get_statistics ( t_PXENV_UNDI_GET_STATISTICS + *undi_get_statistics ) { + DBG ( "PXENV_UNDI_GET_STATISTICS" ); + /* ENSURE_READY ( undi_get_statistics ); */ + undi_get_statistics->Status = PXENV_STATUS_UNSUPPORTED; + return PXENV_EXIT_FAILURE; +} + +/* PXENV_UNDI_CLEAR_STATISTICS + * + * Status: won't implement (would require driver API changes for no + * real benefit) + */ +PXENV_EXIT_t pxenv_undi_clear_statistics ( t_PXENV_UNDI_CLEAR_STATISTICS + *undi_clear_statistics ) { + DBG ( "PXENV_UNDI_CLEAR_STATISTICS" ); + /* ENSURE_READY ( undi_clear_statistics ); */ + undi_clear_statistics->Status = PXENV_STATUS_UNSUPPORTED; + return PXENV_EXIT_FAILURE; +} + +/* PXENV_UNDI_INITIATE_DIAGS + * + * Status: won't implement (would require driver API changes for no + * real benefit) + */ +PXENV_EXIT_t pxenv_undi_initiate_diags ( t_PXENV_UNDI_INITIATE_DIAGS + *undi_initiate_diags ) { + DBG ( "PXENV_UNDI_INITIATE_DIAGS" ); + /* ENSURE_READY ( undi_initiate_diags ); */ + undi_initiate_diags->Status = PXENV_STATUS_UNSUPPORTED; + return PXENV_EXIT_FAILURE; +} + +/* PXENV_UNDI_FORCE_INTERRUPT + * + * Status: working + */ +PXENV_EXIT_t pxenv_undi_force_interrupt ( t_PXENV_UNDI_FORCE_INTERRUPT + *undi_force_interrupt ) { + DBG ( "PXENV_UNDI_FORCE_INTERRUPT" ); + ENSURE_READY ( undi_force_interrupt ); + + eth_irq ( FORCE ); + undi_force_interrupt->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_UNDI_GET_MCAST_ADDRESS + * + * Status: stub (no PXE multicast support) + */ +PXENV_EXIT_t pxenv_undi_get_mcast_address ( t_PXENV_UNDI_GET_MCAST_ADDRESS + *undi_get_mcast_address ) { + DBG ( "PXENV_UNDI_GET_MCAST_ADDRESS" ); + /* ENSURE_READY ( 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 ( t_PXENV_UNDI_GET_NIC_TYPE + *undi_get_nic_type ) { + struct dev *dev = &nic.dev; + + DBG ( "PXENV_UNDI_GET_NIC_TYPE" ); + ENSURE_READY ( undi_get_nic_type ); + + if ( dev->to_probe == PROBE_PCI ) { + struct pci_device *pci = &dev->state.pci.dev; + + undi_get_nic_type->NicType = PCI_NIC; + undi_get_nic_type->info.pci.Vendor_ID = pci->vendor; + undi_get_nic_type->info.pci.Dev_ID = pci->dev_id; + undi_get_nic_type->info.pci.Base_Class = pci->class >> 8; + undi_get_nic_type->info.pci.Sub_Class = pci->class & 0xff; + undi_get_nic_type->info.pci.BusDevFunc = + ( pci->bus << 8 ) | pci->devfn; + /* Cheat: these fields are probably unnecessary, and + * would require adding extra code to pci.c. + */ + undi_get_nic_type->info.pci.Prog_Intf = 0; + undi_get_nic_type->info.pci.Rev = 0; + undi_get_nic_type->info.pci.SubVendor_ID = 0xffff; + undi_get_nic_type->info.pci.SubDevice_ID = 0xffff; + } else if ( dev->to_probe == PROBE_ISA ) { + /* const struct isa_driver *isa = dev->state.isa.driver; */ + + undi_get_nic_type->NicType = PnP_NIC; + /* Don't think anything fills these fields in, and + * probably no-one will ever be interested in them. + */ + undi_get_nic_type->info.pnp.EISA_Dev_ID = 0; + undi_get_nic_type->info.pnp.Base_Class = 0; + undi_get_nic_type->info.pnp.Sub_Class = 0; + undi_get_nic_type->info.pnp.Prog_Intf = 0; + undi_get_nic_type->info.pnp.CardSelNum = 0; + } else { + /* PXESPEC: There doesn't seem to be an "unknown type" + * defined. + */ + undi_get_nic_type->NicType = 0; + } + 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 ( t_PXENV_UNDI_GET_IFACE_INFO + *undi_get_iface_info ) { + DBG ( "PXENV_UNDI_GET_IFACE_INFO" ); + ENSURE_READY ( undi_get_iface_info ); + + /* Just hand back some info, doesn't really matter what it is. + * Most PXE stacks seem to take this approach. + */ + sprintf ( undi_get_iface_info->IfaceType, "Etherboot" ); + 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_ISR + * + * Status: working + */ +PXENV_EXIT_t pxenv_undi_isr ( t_PXENV_UNDI_ISR *undi_isr ) { + media_header_t *media_header = (media_header_t*)nic.packet; + + DBG ( "PXENV_UNDI_ISR" ); + /* We can't call ENSURE_READY, because this could be being + * called as part of an interrupt service routine. Instead, + * we should simply die if we're not READY. + */ + if ( ( pxe_stack == NULL ) || ( pxe_stack->state < READY ) ) { + undi_isr->Status = PXENV_STATUS_UNDI_INVALID_STATE; + return PXENV_EXIT_FAILURE; + } + + /* 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 : + /* Is there a packet waiting? If so, disable + * interrupts on the NIC and return "it's ours". Do + * *not* necessarily acknowledge the interrupt; this + * can happen later when eth_poll(1) is called. As + * long as the interrupt is masked off so that it + * doesn't immediately retrigger the 8259A then all + * should be well. + */ + DBG ( " START" ); + if ( eth_poll ( 0 ) ) { + DBG ( " OURS" ); + eth_irq ( DISABLE ); + undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS; + } else { + DBG ( " NOT_OURS" ); + undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_NOT_OURS; + } + break; + case PXENV_UNDI_ISR_IN_PROCESS : + /* Call poll(), return packet. If no packet, return "done". + */ + DBG ( " PROCESS" ); + if ( eth_poll ( 1 ) ) { + DBG ( " RECEIVE %d", nic.packetlen ); + if ( nic.packetlen > sizeof(pxe_stack->packet) ) { + /* Should never happen */ + undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE; + undi_isr->Status = + PXENV_STATUS_OUT_OF_RESOURCES; + return PXENV_EXIT_FAILURE; + } + undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE; + undi_isr->BufferLength = nic.packetlen; + undi_isr->FrameLength = nic.packetlen; + undi_isr->FrameHeaderLength = ETH_HLEN; + memcpy ( pxe_stack->packet, nic.packet, nic.packetlen); + PTR_TO_SEGOFF16 ( pxe_stack->packet, undi_isr->Frame ); + switch ( ntohs(media_header->nstype) ) { + case IP : undi_isr->ProtType = P_IP; break; + case ARP : undi_isr->ProtType = P_ARP; break; + case RARP : undi_isr->ProtType = P_RARP; break; + default : undi_isr->ProtType = P_UNKNOWN; + } + if ( memcmp ( media_header->dest, broadcast_mac, + sizeof(broadcast_mac) ) ) { + undi_isr->PktType = XMT_BROADCAST; + } else { + undi_isr->PktType = XMT_DESTADDR; + } + break; + } else { + /* No break - fall through to IN_GET_NEXT */ + } + case PXENV_UNDI_ISR_IN_GET_NEXT : + /* We only ever return one frame at a time */ + DBG ( " GET_NEXT DONE" ); + /* Re-enable interrupts */ + eth_irq ( ENABLE ); + /* Force an interrupt if there's a packet still + * waiting, since we only handle one packet per + * interrupt. + */ + if ( eth_poll ( 0 ) ) { + DBG ( " (RETRIGGER)" ); + eth_irq ( FORCE ); + } + undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE; + break; + default : + /* 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; +} + +/* PXENV_STOP_UNDI + * + * Status: working + */ +PXENV_EXIT_t pxenv_stop_undi ( t_PXENV_STOP_UNDI *stop_undi ) { + DBG ( "PXENV_STOP_UNDI" ); + + if ( ! ensure_pxe_state(CAN_UNLOAD) ) { + stop_undi->Status = PXENV_STATUS_KEEP_UNDI; + return PXENV_EXIT_FAILURE; + } + + stop_undi->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_TFTP_OPEN + * + * Status: working + */ +PXENV_EXIT_t pxenv_tftp_open ( t_PXENV_TFTP_OPEN *tftp_open ) { + struct tftpreq_info_t request; + struct tftpblk_info_t block; + + DBG ( "PXENV_TFTP_OPEN" ); + ENSURE_READY ( tftp_open ); + + /* Change server address if different */ + if ( tftp_open->ServerIPAddress && + tftp_open->ServerIPAddress!=arptable[ARP_SERVER].ipaddr.s_addr ) { + memset(arptable[ARP_SERVER].node, 0, ETH_ALEN ); /* kill arp */ + arptable[ARP_SERVER].ipaddr.s_addr=tftp_open->ServerIPAddress; + } + /* Ignore gateway address; we can route properly */ + /* Fill in request structure */ + request.name = tftp_open->FileName; + request.port = ntohs(tftp_open->TFTPPort); +#ifdef WORK_AROUND_BPBATCH_BUG + /* Force use of port 69; BpBatch tries to use port 4 for some + * bizarre reason. */ + request.port = TFTP_PORT; +#endif + request.blksize = tftp_open->PacketSize; + DBG ( " %@:%d/%s (%d)", tftp_open->ServerIPAddress, + request.port, request.name, request.blksize ); + if ( !request.blksize ) request.blksize = TFTP_DEFAULTSIZE_PACKET; + /* Make request and get first packet */ + if ( !tftp_block ( &request, &block ) ) { + tftp_open->Status = PXENV_STATUS_TFTP_FILE_NOT_FOUND; + return PXENV_EXIT_FAILURE; + } + /* Fill in PacketSize */ + tftp_open->PacketSize = request.blksize; + /* Store first block for later retrieval by TFTP_READ */ + pxe_stack->tftpdata.magic_cookie = PXE_TFTP_MAGIC_COOKIE; + pxe_stack->tftpdata.len = block.len; + pxe_stack->tftpdata.eof = block.eof; + memcpy ( pxe_stack->tftpdata.data, block.data, block.len ); + + tftp_open->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_TFTP_CLOSE + * + * Status: working + */ +PXENV_EXIT_t pxenv_tftp_close ( t_PXENV_TFTP_CLOSE *tftp_close ) { + DBG ( "PXENV_TFTP_CLOSE" ); + ENSURE_READY ( tftp_close ); + tftp_close->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_TFTP_READ + * + * Status: working + */ +PXENV_EXIT_t pxenv_tftp_read ( t_PXENV_TFTP_READ *tftp_read ) { + struct tftpblk_info_t block; + + DBG ( "PXENV_TFTP_READ" ); + ENSURE_READY ( tftp_read ); + + /* Do we have a block pending */ + if ( pxe_stack->tftpdata.magic_cookie == PXE_TFTP_MAGIC_COOKIE ) { + block.data = pxe_stack->tftpdata.data; + block.len = pxe_stack->tftpdata.len; + block.eof = pxe_stack->tftpdata.eof; + block.block = 1; /* Will be the first block */ + pxe_stack->tftpdata.magic_cookie = 0; + } else { + if ( !tftp_block ( NULL, &block ) ) { + tftp_read->Status = PXENV_STATUS_TFTP_FILE_NOT_FOUND; + return PXENV_EXIT_FAILURE; + } + } + + /* Return data */ + tftp_read->PacketNumber = block.block; + tftp_read->BufferSize = block.len; + memcpy ( SEGOFF16_TO_PTR(tftp_read->Buffer), block.data, block.len ); + DBG ( " %d to %hx:%hx", block.len, tftp_read->Buffer.segment, + tftp_read->Buffer.offset ); + + tftp_read->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_TFTP_READ_FILE + * + * Status: working + */ + +int pxe_tftp_read_block ( unsigned char *data, unsigned int block __unused, unsigned int len, int eof ) { + if ( pxe_stack->readfile.buffer ) { + if ( pxe_stack->readfile.offset + len >= + pxe_stack->readfile.bufferlen ) return -1; + memcpy ( pxe_stack->readfile.buffer + + pxe_stack->readfile.offset, data, len ); + } + pxe_stack->readfile.offset += len; + return eof ? 0 : 1; +} + +PXENV_EXIT_t pxenv_tftp_read_file ( t_PXENV_TFTP_READ_FILE *tftp_read_file ) { + int rc; + + DBG ( "PXENV_TFTP_READ_FILE %s to [%x,%x)", tftp_read_file->FileName, + tftp_read_file->Buffer, tftp_read_file->Buffer + tftp_read_file->BufferSize ); + ENSURE_READY ( tftp_read_file ); + + /* inserted by Klaus Wittemeier */ + /* KERNEL_BUF stores the name of the last required file */ + /* This is a fix to make Microsoft Remote Install Services work (RIS) */ + memcpy(KERNEL_BUF, tftp_read_file->FileName, sizeof(KERNEL_BUF)); + /* end of insertion */ + + pxe_stack->readfile.buffer = phys_to_virt ( tftp_read_file->Buffer ); + pxe_stack->readfile.bufferlen = tftp_read_file->BufferSize; + pxe_stack->readfile.offset = 0; + + rc = tftp ( tftp_read_file->FileName, pxe_tftp_read_block ); + if ( rc ) { + tftp_read_file->Status = PXENV_STATUS_FAILURE; + return PXENV_EXIT_FAILURE; + } + tftp_read_file->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_TFTP_GET_FSIZE + * + * Status: working, though ugly (we actually read the whole file, + * because it's too ugly to make Etherboot request the tsize option + * and hand it to us). + */ +PXENV_EXIT_t pxenv_tftp_get_fsize ( t_PXENV_TFTP_GET_FSIZE *tftp_get_fsize ) { + int rc; + + DBG ( "PXENV_TFTP_GET_FSIZE" ); + ENSURE_READY ( tftp_get_fsize ); + + pxe_stack->readfile.buffer = NULL; + pxe_stack->readfile.bufferlen = 0; + pxe_stack->readfile.offset = 0; + + rc = tftp ( tftp_get_fsize->FileName, pxe_tftp_read_block ); + if ( rc ) { + tftp_get_fsize->FileSize = 0; + tftp_get_fsize->Status = PXENV_STATUS_FAILURE; + return PXENV_EXIT_FAILURE; + } + tftp_get_fsize->FileSize = pxe_stack->readfile.offset; + tftp_get_fsize->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_UDP_OPEN + * + * Status: working + */ +PXENV_EXIT_t pxenv_udp_open ( t_PXENV_UDP_OPEN *udp_open ) { + DBG ( "PXENV_UDP_OPEN" ); + ENSURE_READY ( udp_open ); + + if ( udp_open->src_ip && + udp_open->src_ip != arptable[ARP_CLIENT].ipaddr.s_addr ) { + /* Overwrite our IP address */ + DBG ( " with new IP %@", udp_open->src_ip ); + arptable[ARP_CLIENT].ipaddr.s_addr = udp_open->src_ip; + } + + udp_open->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_UDP_CLOSE + * + * Status: working + */ +PXENV_EXIT_t pxenv_udp_close ( t_PXENV_UDP_CLOSE *udp_close ) { + DBG ( "PXENV_UDP_CLOSE" ); + ENSURE_READY ( udp_close ); + udp_close->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_UDP_READ + * + * Status: working + */ +int await_pxe_udp ( int ival __unused, void *ptr, + unsigned short ptype __unused, + struct iphdr *ip, struct udphdr *udp, + struct tcphdr *tcp __unused ) { + t_PXENV_UDP_READ *udp_read = (t_PXENV_UDP_READ*)ptr; + uint16_t d_port; + size_t size; + + /* Ignore non-UDP packets */ + if ( !udp ) { + DBG ( " non-UDP" ); + return 0; + } + + /* Check dest_ip */ + if ( udp_read->dest_ip && ( udp_read->dest_ip != ip->dest.s_addr ) ) { + DBG ( " wrong dest IP (got %@, wanted %@)", + ip->dest.s_addr, udp_read->dest_ip ); + return 0; + } + + /* Check dest_port */ + d_port = ntohs ( udp_read->d_port ); + if ( d_port && ( d_port != ntohs(udp->dest) ) ) { + DBG ( " wrong dest port (got %d, wanted %d)", + ntohs(udp->dest), d_port ); + return 0; + } + + /* Copy packet to buffer and fill in information */ + udp_read->src_ip = ip->src.s_addr; + udp_read->s_port = udp->src; /* Both in network order */ + size = ntohs(udp->len) - sizeof(*udp); + /* Workaround: NTLDR expects us to fill these in, even though + * PXESPEC clearly defines them as input parameters. + */ + udp_read->dest_ip = ip->dest.s_addr; + udp_read->d_port = udp->dest; + DBG ( " %@:%d->%@:%d (%d)", + udp_read->src_ip, ntohs(udp_read->s_port), + udp_read->dest_ip, ntohs(udp_read->d_port), size ); + if ( udp_read->buffer_size < size ) { + /* PXESPEC: what error code should we actually return? */ + DBG ( " buffer too small (%d)", udp_read->buffer_size ); + udp_read->Status = PXENV_STATUS_OUT_OF_RESOURCES; + return 0; + } + memcpy ( SEGOFF16_TO_PTR ( udp_read->buffer ), &udp->payload, size ); + udp_read->buffer_size = size; + + return 1; +} + +PXENV_EXIT_t pxenv_udp_read ( t_PXENV_UDP_READ *udp_read ) { + DBG ( "PXENV_UDP_READ" ); + ENSURE_READY ( udp_read ); + + /* Use await_reply with a timeout of zero */ + /* Allow await_reply to change Status if necessary */ + udp_read->Status = PXENV_STATUS_FAILURE; + if ( ! await_reply ( await_pxe_udp, 0, udp_read, 0 ) ) { + return PXENV_EXIT_FAILURE; + } + + udp_read->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_UDP_WRITE + * + * Status: working + */ +PXENV_EXIT_t pxenv_udp_write ( t_PXENV_UDP_WRITE *udp_write ) { + uint16_t src_port; + uint16_t dst_port; + struct udppacket *packet = (struct udppacket *)nic.packet; + int packet_size; + + DBG ( "PXENV_UDP_WRITE" ); + ENSURE_READY ( udp_write ); + + /* PXE spec says source port is 2069 if not specified */ + src_port = ntohs(udp_write->src_port); + if ( src_port == 0 ) src_port = 2069; + dst_port = ntohs(udp_write->dst_port); + DBG ( " %d->%@:%d (%d)", src_port, udp_write->ip, dst_port, + udp_write->buffer_size ); + + /* 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. + */ + + /* Copy payload to packet buffer */ + packet_size = ( (void*)&packet->payload - (void*)packet ) + + udp_write->buffer_size; + if ( packet_size > ETH_FRAME_LEN ) { + udp_write->Status = PXENV_STATUS_OUT_OF_RESOURCES; + return PXENV_EXIT_FAILURE; + } + memcpy ( &packet->payload, SEGOFF16_TO_PTR(udp_write->buffer), + udp_write->buffer_size ); + + /* Transmit packet */ + if ( ! udp_transmit ( udp_write->ip, src_port, dst_port, + packet_size, packet ) ) { + udp_write->Status = PXENV_STATUS_FAILURE; + return PXENV_EXIT_FAILURE; + } + + udp_write->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_UNLOAD_STACK + * + * Status: working + */ +PXENV_EXIT_t pxenv_unload_stack ( t_PXENV_UNLOAD_STACK *unload_stack ) { + int success; + + DBG ( "PXENV_UNLOAD_STACK" ); + success = ensure_pxe_state ( CAN_UNLOAD ); + + /* We need to call cleanup() at some point. The network card + * has already been disabled by ENSURE_CAN_UNLOAD(), but for + * the sake of completeness we should call the console_fini() + * etc. that are part of cleanup(). + * + * There seems to be a lack of consensus on which is the final + * PXE API call to make, but it's a fairly safe bet that all + * the potential shutdown sequences will include a call to + * PXENV_UNLOAD_STACK at some point, so we may as well do it + * here. + */ + cleanup(); + + if ( ! success ) { + unload_stack->Status = PXENV_STATUS_KEEP_ALL; + return PXENV_EXIT_FAILURE; + } + + unload_stack->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_GET_CACHED_INFO + * + * Status: working + */ +PXENV_EXIT_t pxenv_get_cached_info ( t_PXENV_GET_CACHED_INFO + *get_cached_info ) { + BOOTPLAYER *cached_info = &pxe_stack->cached_info; + DBG ( "PXENV_GET_CACHED_INFO %d", get_cached_info->PacketType ); + ENSURE_READY ( get_cached_info ); + + /* Fill in cached_info structure in our pxe_stack */ + + /* I don't think there's actually any way we can be called in + * the middle of a DHCP request... + */ + cached_info->opcode = BOOTP_REP; + /* We only have Ethernet drivers */ + cached_info->Hardware = ETHER_TYPE; + cached_info->Hardlen = ETH_ALEN; + /* PXESPEC: "Client sets" says the spec, but who's filling in + * this structure? It ain't the client. + */ + cached_info->Gatehops = 0; + cached_info->ident = 0; + cached_info->seconds = 0; + cached_info->Flags = BOOTP_BCAST; + /* PXESPEC: What do 'Client' and 'Your' IP address refer to? */ + cached_info->cip = arptable[ARP_CLIENT].ipaddr.s_addr; + cached_info->yip = arptable[ARP_CLIENT].ipaddr.s_addr; + cached_info->sip = arptable[ARP_SERVER].ipaddr.s_addr; + /* PXESPEC: Does "GIP" mean "Gateway" or "Relay agent"? */ + cached_info->gip = arptable[ARP_GATEWAY].ipaddr.s_addr; + memcpy ( cached_info->CAddr, arptable[ARP_CLIENT].node, ETH_ALEN ); + /* Nullify server name */ + cached_info->Sname[0] = '\0'; + memcpy ( cached_info->bootfile, KERNEL_BUF, + sizeof(cached_info->bootfile) ); + /* Copy DHCP vendor options */ + memcpy ( &cached_info->vendor.d, BOOTP_DATA_ADDR->bootp_reply.bp_vend, + sizeof(cached_info->vendor.d) ); + + /* Copy to user-specified buffer, or set pointer to our buffer */ + get_cached_info->BufferLimit = sizeof(*cached_info); + /* PXESPEC: says to test for Buffer == NULL *and* BufferSize = + * 0, but what are we supposed to do with a null buffer of + * non-zero size?! + */ + if ( IS_NULL_SEGOFF16 ( get_cached_info->Buffer ) ) { + /* Point back to our buffer */ + PTR_TO_SEGOFF16 ( cached_info, get_cached_info->Buffer ); + get_cached_info->BufferSize = sizeof(*cached_info); + } else { + /* Copy to user buffer */ + size_t size = sizeof(*cached_info); + void *buffer = SEGOFF16_TO_PTR ( get_cached_info->Buffer ); + if ( get_cached_info->BufferSize < size ) + size = get_cached_info->BufferSize; + DBG ( " to %x", virt_to_phys ( buffer ) ); + memcpy ( buffer, cached_info, size ); + /* PXESPEC: Should we return an error if the user + * buffer is too small? We do return the actual size + * of the buffer via BufferLimit, so the user does + * have a way to detect this already. + */ + } + + get_cached_info->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_RESTART_TFTP + * + * Status: working + */ +PXENV_EXIT_t pxenv_restart_tftp ( t_PXENV_RESTART_TFTP *restart_tftp ) { + PXENV_EXIT_t tftp_exit; + + DBG ( "PXENV_RESTART_TFTP" ); + ENSURE_READY ( restart_tftp ); + + /* Words cannot describe the complete mismatch between the PXE + * specification and any possible version of reality... + */ + restart_tftp->Buffer = PXE_LOAD_ADDRESS; /* Fixed by spec, apparently */ + restart_tftp->BufferSize = get_free_base_memory() - PXE_LOAD_ADDRESS; /* Near enough */ + DBG ( "(" ); + tftp_exit = pxe_api_call ( PXENV_TFTP_READ_FILE, (t_PXENV_ANY*)restart_tftp ); + DBG ( ")" ); + if ( tftp_exit != PXENV_EXIT_SUCCESS ) return tftp_exit; + + /* Fire up the new NBP */ + restart_tftp->Status = xstartpxe(); + + /* Not sure what "SUCCESS" actually means, since we can only + * return if the new NBP failed to boot... + */ + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_START_BASE + * + * Status: won't implement (requires major structural changes) + */ +PXENV_EXIT_t pxenv_start_base ( t_PXENV_START_BASE *start_base ) { + DBG ( "PXENV_START_BASE" ); + /* ENSURE_READY ( start_base ); */ + start_base->Status = PXENV_STATUS_UNSUPPORTED; + return PXENV_EXIT_FAILURE; +} + +/* PXENV_STOP_BASE + * + * Status: working + */ +PXENV_EXIT_t pxenv_stop_base ( t_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; +} + +/* PXENV_UNDI_LOADER + * + * Status: working + * + * NOTE: This is not a genuine PXE API call; the loader has a separate + * entry point. However, to simplify the mapping of the PXE API to + * the internal Etherboot API, both are directed through the same + * interface. + */ +PXENV_EXIT_t pxenv_undi_loader ( undi_loader_t *loader ) { + uint32_t loader_phys = virt_to_phys ( loader ); + + DBG ( "PXENV_UNDI_LOADER" ); + + /* Set UNDI DS as our real-mode stack */ + use_undi_ds_for_rm_stack ( loader->undi_ds ); + + /* FIXME: These lines are borrowed from main.c. There should + * probably be a single initialise() function that does all + * this, but it's currently split interestingly between main() + * and main_loop()... + */ + console_init(); + cpu_setup(); + setup_timers(); + gateA20_set(); + print_config(); + get_memsizes(); + cleanup(); + relocate(); + cleanup(); + console_init(); + init_heap(); + + /* We have relocated; the loader pointer is now invalid */ + loader = phys_to_virt ( loader_phys ); + + /* Install PXE stack to area specified by NBP */ + install_pxe_stack ( VIRTUAL ( loader->undi_cs, 0 ) ); + + /* Call pxenv_start_undi to set parameters. Why the hell PXE + * requires these parameters to be provided twice is beyond + * the wit of any sane man. Don't worry if it fails; the NBP + * should call PXENV_START_UNDI separately anyway. + */ + pxenv_start_undi ( &loader->start_undi ); + /* Unhook stack; the loader is not meant to hook int 1a etc, + * but the call the pxenv_start_undi will cause it to happen. + */ + ENSURE_CAN_UNLOAD ( loader ); + + /* Fill in addresses of !PXE and PXENV+ structures */ + PTR_TO_SEGOFF16 ( &pxe_stack->pxe, loader->pxe_ptr ); + PTR_TO_SEGOFF16 ( &pxe_stack->pxenv, loader->pxenv_ptr ); + + loader->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* API call dispatcher + * + * Status: complete + */ +PXENV_EXIT_t pxe_api_call ( int opcode, t_PXENV_ANY *params ) { + PXENV_EXIT_t ret = PXENV_EXIT_FAILURE; + + /* Set default status in case child routine fails to do so */ + params->Status = PXENV_STATUS_FAILURE; + + DBG ( "[" ); + + /* Hand off to relevant API routine */ + switch ( opcode ) { + case PXENV_START_UNDI: + ret = pxenv_start_undi ( ¶ms->start_undi ); + break; + case PXENV_UNDI_STARTUP: + ret = pxenv_undi_startup ( ¶ms->undi_startup ); + break; + case PXENV_UNDI_CLEANUP: + ret = pxenv_undi_cleanup ( ¶ms->undi_cleanup ); + break; + case PXENV_UNDI_INITIALIZE: + ret = pxenv_undi_initialize ( ¶ms->undi_initialize ); + break; + case PXENV_UNDI_RESET_ADAPTER: + ret = pxenv_undi_reset_adapter ( ¶ms->undi_reset_adapter ); + break; + case PXENV_UNDI_SHUTDOWN: + ret = pxenv_undi_shutdown ( ¶ms->undi_shutdown ); + break; + case PXENV_UNDI_OPEN: + ret = pxenv_undi_open ( ¶ms->undi_open ); + break; + case PXENV_UNDI_CLOSE: + ret = pxenv_undi_close ( ¶ms->undi_close ); + break; + case PXENV_UNDI_TRANSMIT: + ret = pxenv_undi_transmit ( ¶ms->undi_transmit ); + break; + case PXENV_UNDI_SET_MCAST_ADDRESS: + ret = pxenv_undi_set_mcast_address ( + ¶ms->undi_set_mcast_address ); + break; + case PXENV_UNDI_SET_STATION_ADDRESS: + ret = pxenv_undi_set_station_address ( + ¶ms->undi_set_station_address ); + break; + case PXENV_UNDI_SET_PACKET_FILTER: + ret = pxenv_undi_set_packet_filter ( + ¶ms->undi_set_packet_filter ); + break; + case PXENV_UNDI_GET_INFORMATION: + ret = pxenv_undi_get_information ( + ¶ms->undi_get_information ); + break; + case PXENV_UNDI_GET_STATISTICS: + ret = pxenv_undi_get_statistics ( + ¶ms->undi_get_statistics ); + break; + case PXENV_UNDI_CLEAR_STATISTICS: + ret = pxenv_undi_clear_statistics ( + ¶ms->undi_clear_statistics ); + break; + case PXENV_UNDI_INITIATE_DIAGS: + ret = pxenv_undi_initiate_diags ( + ¶ms->undi_initiate_diags ); + break; + case PXENV_UNDI_FORCE_INTERRUPT: + ret = pxenv_undi_force_interrupt ( + ¶ms->undi_force_interrupt ); + break; + case PXENV_UNDI_GET_MCAST_ADDRESS: + ret = pxenv_undi_get_mcast_address ( + ¶ms->undi_get_mcast_address ); + break; + case PXENV_UNDI_GET_NIC_TYPE: + ret = pxenv_undi_get_nic_type ( ¶ms->undi_get_nic_type ); + break; + case PXENV_UNDI_GET_IFACE_INFO: + ret = pxenv_undi_get_iface_info ( + ¶ms->undi_get_iface_info ); + break; + case PXENV_UNDI_ISR: + ret = pxenv_undi_isr ( ¶ms->undi_isr ); + break; + case PXENV_STOP_UNDI: + ret = pxenv_stop_undi ( ¶ms->stop_undi ); + break; + case PXENV_TFTP_OPEN: + ret = pxenv_tftp_open ( ¶ms->tftp_open ); + break; + case PXENV_TFTP_CLOSE: + ret = pxenv_tftp_close ( ¶ms->tftp_close ); + break; + case PXENV_TFTP_READ: + ret = pxenv_tftp_read ( ¶ms->tftp_read ); + break; + case PXENV_TFTP_READ_FILE: + ret = pxenv_tftp_read_file ( ¶ms->tftp_read_file ); + break; + case PXENV_TFTP_GET_FSIZE: + ret = pxenv_tftp_get_fsize ( ¶ms->tftp_get_fsize ); + break; + case PXENV_UDP_OPEN: + ret = pxenv_udp_open ( ¶ms->udp_open ); + break; + case PXENV_UDP_CLOSE: + ret = pxenv_udp_close ( ¶ms->udp_close ); + break; + case PXENV_UDP_READ: + ret = pxenv_udp_read ( ¶ms->udp_read ); + break; + case PXENV_UDP_WRITE: + ret = pxenv_udp_write ( ¶ms->udp_write ); + break; + case PXENV_UNLOAD_STACK: + ret = pxenv_unload_stack ( ¶ms->unload_stack ); + break; + case PXENV_GET_CACHED_INFO: + ret = pxenv_get_cached_info ( ¶ms->get_cached_info ); + break; + case PXENV_RESTART_TFTP: + ret = pxenv_restart_tftp ( ¶ms->restart_tftp ); + break; + case PXENV_START_BASE: + ret = pxenv_start_base ( ¶ms->start_base ); + break; + case PXENV_STOP_BASE: + ret = pxenv_stop_base ( ¶ms->stop_base ); + break; + case PXENV_UNDI_LOADER: + ret = pxenv_undi_loader ( ¶ms->loader ); + break; + + default: + DBG ( "PXENV_UNKNOWN_%hx", opcode ); + params->Status = PXENV_STATUS_UNSUPPORTED; + ret = PXENV_EXIT_FAILURE; + break; + } + + if ( params->Status != PXENV_STATUS_SUCCESS ) { + DBG ( " %hx", params->Status ); + } + if ( ret != PXENV_EXIT_SUCCESS ) { + DBG ( ret == PXENV_EXIT_FAILURE ? " err" : " ??" ); + } + DBG ( "]" ); + + return ret; +} + +#endif /* PXE_EXPORT */ diff --git a/src/core/relocate.c b/src/core/relocate.c new file mode 100644 index 00000000..d20846a8 --- /dev/null +++ b/src/core/relocate.c @@ -0,0 +1,103 @@ +#ifndef NORELOCATE + +#include "etherboot.h" + +/* by Eric Biederman */ + +/* On some platforms etherboot is compiled as a shared library, and we use + * the ELF pic support to make it relocateable. This works very nicely + * for code, but since no one has implemented PIC data yet pointer + * values in variables are a a problem. Global variables are a + * pain but the return addresses on the stack are the worst. On these + * platforms relocate_to will restart etherboot, to ensure the stack + * is reinitialize and hopefully get the global variables + * appropriately reinitialized as well. + * + */ + +void relocate(void) +{ + unsigned long addr, eaddr, size; + unsigned i; + /* 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. + */ + /* First find the size of etherboot */ + addr = virt_to_phys(_text); + eaddr = virt_to_phys(_end); + size = (eaddr - addr + 0xf) & ~0xf; + + /* If the current etherboot is beyond MAX_ADDR pretend it is + * at the lowest possible address. + */ + if (eaddr > MAX_ADDR) { + eaddr = 0; + } + + for(i = 0; i < meminfo.map_count; i++) { + unsigned long r_start, r_end; + if (meminfo.map[i].type != E820_RAM) { + continue; + } + if (meminfo.map[i].addr > MAX_ADDR) { + continue; + } + if (meminfo.map[i].size > MAX_ADDR) { + continue; + } + r_start = meminfo.map[i].addr; + r_end = r_start + meminfo.map[i].size; + /* Make the addresses 16 byte (128 bit) aligned */ + r_start = (r_start + 15) & ~15; + r_end = r_end & ~15; + if (r_end < r_start) { + r_end = MAX_ADDR; + } + if (r_end < size) { + /* Avoid overflow weirdness when r_end - size < 0 */ + continue; + } + /* Shrink the range down to use only even megabytes + * (i.e. A20=0). + */ + if ( r_end & 0x100000 ) { + /* If r_end is in an odd megabyte, round down + * r_end to the top of the next even megabyte. + */ + r_end = r_end & ~0xfffff; + } else if ( ( r_end - size ) & 0x100000 ) { + /* If r_end is in an even megabyte, but the + * start of Etherboot would be in an odd + * megabyte, round down to the top of the next + * even megabyte. + */ + r_end = ( r_end - 0x100000 ) & ~0xfffff; + } + /* If we have rounded down r_end below r_ start, skip + * this block. + */ + if ( r_end < r_start ) { + continue; + } + if (eaddr < r_end - size) { + addr = r_end - size; + eaddr = r_end; + } + } + if (addr != virt_to_phys(_text)) { + unsigned long old_addr = virt_to_phys(_text); + printf("Relocating _text from: [%lx,%lx) to [%lx,%lx)\n", + old_addr, virt_to_phys(_end), + addr, eaddr); + arch_relocate_to(addr); + cleanup(); + relocate_to(addr); + arch_relocated_from(old_addr); + } +} + +#endif /* NORELOCATE */ diff --git a/src/core/serial.c b/src/core/serial.c new file mode 100644 index 00000000..72207e8d --- /dev/null +++ b/src/core/serial.c @@ -0,0 +1,236 @@ +#include "etherboot.h" +#include "timer.h" +#ifdef CONSOLE_SERIAL + +/* + * 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(). + */ + +static int found = 0; + +#if defined(COMCONSOLE) +#undef UART_BASE +#define UART_BASE COMCONSOLE +#endif + +#ifndef UART_BASE +#error UART_BASE not defined +#endif + +#if defined(CONSPEED) +#undef UART_BAUD +#define UART_BAUD CONSPEED +#endif + +#ifndef UART_BAUD +#define UART_BAUD 115200 +#endif + +#if ((115200%UART_BAUD) != 0) +#error Bad ttys0 baud rate +#endif + +#define COMBRD (115200/UART_BAUD) + +/* Line Control Settings */ +#ifndef COMPARM +/* Set 8bit, 1 stop bit, no parity */ +#define COMPARM 0x03 +#endif + +#define UART_LCS COMPARM + +/* 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 + +/* + * void serial_putc(int ch); + * Write character `ch' to port UART_BASE. + */ +void serial_putc(int ch) +{ + int i; + int status; + if (!found) { + /* no serial interface */ + return; + } + 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. + */ +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. + */ +int serial_ischar(void) +{ + int status; + if (!found) + return 0; + 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 CONSPEED, line settings 8N1. + */ +int serial_init(void) +{ + int initialized = 0; + int status; + int divisor, lcs; + + if (found) + return 1; + + 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 CONSPEED, 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) + goto out; + uart_writeb(0x55, UART_BASE + UART_DLL); + if (uart_readb(UART_BASE + UART_DLL) != 0x55) + goto out; + uart_writeb(divisor & 0xff, UART_BASE + UART_DLL); + if (uart_readb(UART_BASE + UART_DLL) != (divisor & 0xff)) + goto out; + uart_writeb(0xaa, UART_BASE + UART_DLM); + if (uart_readb(UART_BASE + UART_DLM) != 0xaa) + goto out; + uart_writeb(0x55, UART_BASE + UART_DLM); + if (uart_readb(UART_BASE + UART_DLM) != 0x55) + goto out; + uart_writeb((divisor >> 8) & 0xff, UART_BASE + UART_DLM); + if (uart_readb(UART_BASE + UART_DLM) != ((divisor >> 8) & 0xff)) + 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) + */ + uart_readb(UART_BASE + UART_RBR); + /* line status reg */ + status = uart_readb(UART_BASE + UART_LSR); + } while(status & UART_LSR_DR); + initialized = 1; + out: + found = initialized; + return initialized; +} + +/* + * void serial_fini(void); + * Cleanup our use of the serial port, in particular flush the + * output buffer so we don't accidentially loose characters. + */ +void serial_fini(void) +{ + int i, status; + if (!found) { + /* 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)); +} +#endif diff --git a/src/core/string.c b/src/core/string.c new file mode 100644 index 00000000..856f955b --- /dev/null +++ b/src/core/string.c @@ -0,0 +1,540 @@ +/* + * 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 + * + * These are buggy as well.. + * + * * Fri Jun 25 1999, Ingo Oeser + * - Added strsep() which will replace strtok() soon (because strsep() is + * reentrant and should be faster). Use only strsep() in new code, please. + */ + +#include "etherboot.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_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_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_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_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_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_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_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_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) +{ + char *tmp = dest; + + while (count--) + *tmp++ = *src++; + + return dest; +} +#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_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(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 + +#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 diff --git a/src/core/timer.c b/src/core/timer.c new file mode 100644 index 00000000..d4d38ad5 --- /dev/null +++ b/src/core/timer.c @@ -0,0 +1,30 @@ +/* A couple of routines to implement a low-overhead timer for drivers */ + + /* + * 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 "timer.h" + +/* Machine Independant timer helper functions */ + +void mdelay(unsigned int msecs) +{ + unsigned int i; + for(i = 0; i < msecs; i++) { + udelay(1000); + poll_interruptions(); + } +} + +void waiton_timer2(unsigned int ticks) +{ + load_timer2(ticks); + while(timer2_running()) { + poll_interruptions(); + } +} diff --git a/src/core/vsprintf.c b/src/core/vsprintf.c new file mode 100644 index 00000000..b22ae310 --- /dev/null +++ b/src/core/vsprintf.c @@ -0,0 +1,166 @@ +#include "etherboot.h" +#include + +#define LONG_SHIFT ((int)((sizeof(unsigned long)*CHAR_BIT) - 4)) +#define INT_SHIFT ((int)((sizeof(unsigned int)*CHAR_BIT) - 4)) +#define SHRT_SHIFT ((int)((sizeof(unsigned short)*CHAR_BIT) - 4)) +#define CHAR_SHIFT ((int)((sizeof(unsigned char)*CHAR_BIT) - 4)) + +/************************************************************************** +PRINTF and friends + + Formats: + %[#]x - 4 bytes int (8 hex digits, lower case) + %[#]X - 4 bytes int (8 hex digits, upper case) + %[#]lx - 8 bytes long (16 hex digits, lower case) + %[#]lX - 8 bytes long (16 hex digits, upper case) + %[#]hx - 2 bytes int (4 hex digits, lower case) + %[#]hX - 2 bytes int (4 hex digits, upper case) + %[#]hhx - 1 byte int (2 hex digits, lower case) + %[#]hhX - 1 byte int (2 hex digits, upper case) + - optional # prefixes 0x or 0X + %d - decimal int + %c - char + %s - string + %@ - Internet address in ddd.ddd.ddd.ddd notation + %! - Ethernet address in xx:xx:xx:xx:xx:xx notation + Note: width specification ignored +**************************************************************************/ +static int vsprintf(char *buf, const char *fmt, va_list args) +{ + char *p, *s; + s = buf; + for ( ; *fmt != '\0'; ++fmt) { + if (*fmt != '%') { + buf ? *s++ = *fmt : putchar(*fmt); + continue; + } + /* skip width specs */ + fmt++; + while (*fmt >= '0' && *fmt <= '9') + fmt++; + if (*fmt == '.') + fmt++; + while (*fmt >= '0' && *fmt <= '9') + fmt++; + if (*fmt == 's') { + for(p = va_arg(args, char *); *p != '\0'; p++) + buf ? *s++ = *p : putchar(*p); + } + else { /* Length of item is bounded */ + char tmp[40], *q = tmp; + int alt = 0; + int shift = INT_SHIFT; + if (*fmt == '#') { + alt = 1; + fmt++; + } + if (*fmt == 'l') { + shift = LONG_SHIFT; + fmt++; + } + else if (*fmt == 'h') { + shift = SHRT_SHIFT; + fmt++; + if (*fmt == 'h') { + shift = CHAR_SHIFT; + fmt++; + } + } + + /* + * Before each format q points to tmp buffer + * After each format q points past end of item + */ + if ((*fmt | 0x20) == 'x') { + /* With x86 gcc, sizeof(long) == sizeof(int) */ + unsigned long h; + int ncase; + if (shift > INT_SHIFT) { + h = va_arg(args, unsigned long); + } else { + h = va_arg(args, unsigned int); + } + ncase = (*fmt & 0x20); + if (alt) { + *q++ = '0'; + *q++ = 'X' | ncase; + } + for ( ; shift >= 0; shift -= 4) + *q++ = "0123456789ABCDEF"[(h >> shift) & 0xF] | ncase; + } + else if (*fmt == 'd') { + char *r; + long i; + if (shift > INT_SHIFT) { + i = va_arg(args, long); + } else { + i = va_arg(args, int); + } + if (i < 0) { + *q++ = '-'; + i = -i; + } + p = q; /* save beginning of digits */ + do { + *q++ = '0' + (i % 10); + i /= 10; + } while (i); + /* reverse digits, stop in middle */ + r = q; /* don't alter q */ + while (--r > p) { + i = *r; + *r = *p; + *p++ = i; + } + } + else if (*fmt == '@') { + unsigned char *r; + union { + uint32_t l; + unsigned char c[4]; + } u; + u.l = va_arg(args, uint32_t); + for (r = &u.c[0]; r < &u.c[4]; ++r) + q += sprintf(q, "%d.", *r); + --q; + } + else if (*fmt == '!') { + char *r; + p = va_arg(args, char *); + for (r = p + ETH_ALEN; p < r; ++p) + q += sprintf(q, "%hhX:", *p); + --q; + } + else if (*fmt == 'c') + *q++ = va_arg(args, int); + else + *q++ = *fmt; + /* now output the saved string */ + for (p = tmp; p < q; ++p) + buf ? *s++ = *p : putchar(*p); + } + } + if (buf) + *s = '\0'; + return (s - buf); +} + +int sprintf(char *buf, const char *fmt, ...) +{ + va_list args; + int i; + va_start(args, fmt); + i=vsprintf(buf, fmt, args); + va_end(args); + return i; +} + +void printf(const char *fmt, ...) +{ + va_list args; + int i; + va_start(args, fmt); + i=vsprintf(0, fmt, args); + va_end(args); +} diff --git a/src/drivers/disk/filo.c b/src/drivers/disk/filo.c new file mode 100644 index 00000000..8d77d24e --- /dev/null +++ b/src/drivers/disk/filo.c @@ -0,0 +1,58 @@ +#include "etherboot.h" +#include "pci.h" +#include "disk.h" + +/* + * UBL, The Universal Talkware Boot Loader + * Copyright (C) 2000 Universal Talkware Inc. + * Copyright (C) 2002 Eric Biederman + * Add to load filo + * By LYH yhlu@tyan.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + */ + +#ifdef CONFIG_PCI +extern int filo(void); + +static int filo_pci_probe(struct dev *dev, struct pci_device *pci) +{ + struct disk *disk = (struct disk *)dev; + + filo(); + + /* past all of the drives */ + dev->index = 0; + return 0; +} +#define PCI_DEVICE_ID_INTEL_82801CA_11 0x248b + +static struct pci_id ide_controllers[] = { +{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_11, "PIIX4" }, +}; + +static struct pci_driver ide_driver __pci_driver = { + .type = DISK_DRIVER, + .name = "FILO", + .probe = filo_pci_probe, + .ids = ide_controllers, + .id_count = sizeof(ide_controllers)/sizeof(ide_controllers), + .class = PCI_CLASS_STORAGE_IDE, +}; +#endif + diff --git a/src/drivers/disk/floppy.c b/src/drivers/disk/floppy.c new file mode 100644 index 00000000..9cb0a22d --- /dev/null +++ b/src/drivers/disk/floppy.c @@ -0,0 +1,88 @@ +#if (TRY_FLOPPY_FIRST > 0) + +/* + * 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" + +#define BOOTSECT ((char *)0x7C00) +#define BOOTSIG ((unsigned short *)BOOTSECT)[0xFF] +#define PARTTAB ((partentry *)(BOOTSECT+0x1BE)) + +typedef struct partentry { + unsigned char flags; + unsigned char start_head; + unsigned char start_sector; + unsigned char start_cylinder; + unsigned char type; + unsigned char end_head; + unsigned char end_sector; + unsigned char end_cylinder; + unsigned long offset; + unsigned long length; +} partentry; + +static unsigned int disk_read_retry(int dev,int c,int h,int s) +{ + int retry = 3,rc; + + while (retry-- && (rc = pcbios_disk_read(dev,c,h,s,BOOTSECT)) != 0); + if (BOOTSIG != 0xAA55) { + printf("not a boot sector"); + return(0xFFFF); } + return(rc); +} + +int bootdisk(int dev,int part) +{ + int rc; + + disk_init(); + if ((rc = disk_read_retry(dev,0,0,1)) != 0) { + readerr: + if (rc != 0xFFFF) + printf("read error (%#hhX)",rc/256); + return(0); } + if (part) { + partentry *ptr; + + if (part >= 5) { + int i; + for (i = 0, ptr = PARTTAB; ptr->type != 5; ptr++) + if (++i == 4) { + printf("partition not found"); + return(0); } + if ((rc = disk_read_retry(dev, + ptr->start_cylinder, + ptr->start_head, + ptr->start_sector)) != 0) + goto readerr; + part -= 4; } + if (!(ptr = PARTTAB-1+part)->type) { + printf("empty partition"); + return(0); } + if ((rc = disk_read_retry(dev, + ptr->start_cylinder, + ptr->start_head, + ptr->start_sector)) != 0) + goto readerr; } + cleanup(); + gateA20_unset(); + /* Set %edx to device number to emulate BIOS + Fortunately %edx is not used after this */ + __asm__("movl %0,%%edx" : : "g" (dev)); + xstart((unsigned long)BOOTSECT, 0, 0); + return(0); +} + +#endif + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/src/drivers/disk/ide_disk.c b/src/drivers/disk/ide_disk.c new file mode 100644 index 00000000..be0fa310 --- /dev/null +++ b/src/drivers/disk/ide_disk.c @@ -0,0 +1,893 @@ +#include "etherboot.h" +#include "timer.h" +#include "pci.h" +#include "isa.h" +#include "disk.h" + +#define BSY_SET_DURING_SPINUP 1 +/* + * UBL, The Universal Talkware Boot Loader + * Copyright (C) 2000 Universal Talkware Inc. + * Copyright (C) 2002 Eric Biederman + * + * 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 + * + * + */ +struct controller { + uint16_t cmd_base; + uint16_t ctrl_base; +}; + +struct harddisk_info { + struct controller *ctrl; + uint16_t heads; + uint16_t cylinders; + uint16_t sectors_per_track; + uint8_t model_number[41]; + uint8_t slave; + sector_t sectors; + int address_mode; /* am i lba (0x40) or chs (0x00) */ +#define ADDRESS_MODE_CHS 0 +#define ADDRESS_MODE_LBA 1 +#define ADDRESS_MODE_LBA48 2 + int drive_exists; + int slave_absent; + int basedrive; +}; + + +#define IDE_SECTOR_SIZE 0x200 + +#define IDE_BASE0 (0x1F0u) /* primary controller */ +#define IDE_BASE1 (0x170u) /* secondary */ +#define IDE_BASE2 (0x0F0u) /* third */ +#define IDE_BASE3 (0x070u) /* fourth */ + +#define IDE_REG_EXTENDED_OFFSET (0x204u) + +#define IDE_REG_DATA(base) ((ctrl)->cmd_base + 0u) /* word register */ +#define IDE_REG_ERROR(base) ((ctrl)->cmd_base + 1u) +#define IDE_REG_PRECOMP(base) ((ctrl)->cmd_base + 1u) +#define IDE_REG_FEATURE(base) ((ctrl)->cmd_base + 1u) +#define IDE_REG_SECTOR_COUNT(base) ((ctrl)->cmd_base + 2u) +#define IDE_REG_SECTOR_NUMBER(base) ((ctrl)->cmd_base + 3u) +#define IDE_REG_LBA_LOW(base) ((ctrl)->cmd_base + 3u) +#define IDE_REG_CYLINDER_LSB(base) ((ctrl)->cmd_base + 4u) +#define IDE_REG_LBA_MID(base) ((ctrl)->cmd_base + 4u) +#define IDE_REG_CYLINDER_MSB(base) ((ctrl)->cmd_base + 5u) +#define IDE_REG_LBA_HIGH(base) ((ctrl)->cmd_base + 5u) +#define IDE_REG_DRIVEHEAD(base) ((ctrl)->cmd_base + 6u) +#define IDE_REG_DEVICE(base) ((ctrl)->cmd_base + 6u) +#define IDE_REG_STATUS(base) ((ctrl)->cmd_base + 7u) +#define IDE_REG_COMMAND(base) ((ctrl)->cmd_base + 7u) +#define IDE_REG_ALTSTATUS(base) ((ctrl)->ctrl_base + 2u) +#define IDE_REG_DEVICE_CONTROL(base) ((ctrl)->ctrl_base + 2u) + +struct ide_pio_command +{ + uint8_t feature; + uint8_t sector_count; + uint8_t lba_low; + uint8_t lba_mid; + uint8_t lba_high; + uint8_t device; +# define IDE_DH_DEFAULT (0xA0) +# define IDE_DH_HEAD(x) ((x) & 0x0F) +# define IDE_DH_MASTER (0x00) +# define IDE_DH_SLAVE (0x10) +# define IDE_DH_LBA (0x40) +# define IDE_DH_CHS (0x00) + uint8_t command; + uint8_t sector_count2; + uint8_t lba_low2; + uint8_t lba_mid2; + uint8_t lba_high2; +}; + +#define IDE_DEFAULT_COMMAND { 0xFFu, 0x01, 0x00, 0x0000, IDE_DH_DEFAULT } + +#define IDE_ERR_ICRC 0x80 /* ATA Ultra DMA bad CRC */ +#define IDE_ERR_BBK 0x80 /* ATA bad block */ +#define IDE_ERR_UNC 0x40 /* ATA uncorrected error */ +#define IDE_ERR_MC 0x20 /* ATA media change */ +#define IDE_ERR_IDNF 0x10 /* ATA id not found */ +#define IDE_ERR_MCR 0x08 /* ATA media change request */ +#define IDE_ERR_ABRT 0x04 /* ATA command aborted */ +#define IDE_ERR_NTK0 0x02 /* ATA track 0 not found */ +#define IDE_ERR_NDAM 0x01 /* ATA address mark not found */ + +#define IDE_STATUS_BSY 0x80 /* busy */ +#define IDE_STATUS_RDY 0x40 /* ready */ +#define IDE_STATUS_DF 0x20 /* device fault */ +#define IDE_STATUS_WFT 0x20 /* write fault (old name) */ +#define IDE_STATUS_SKC 0x10 /* seek complete */ +#define IDE_STATUS_DRQ 0x08 /* data request */ +#define IDE_STATUS_CORR 0x04 /* corrected */ +#define IDE_STATUS_IDX 0x02 /* index */ +#define IDE_STATUS_ERR 0x01 /* error (ATA) */ +#define IDE_STATUS_CHK 0x01 /* check (ATAPI) */ + +#define IDE_CTRL_HD15 0x08 /* bit should always be set to one */ +#define IDE_CTRL_SRST 0x04 /* soft reset */ +#define IDE_CTRL_NIEN 0x02 /* disable interrupts */ + + +/* Most mandtory and optional ATA commands (from ATA-3), */ + +#define IDE_CMD_CFA_ERASE_SECTORS 0xC0 +#define IDE_CMD_CFA_REQUEST_EXT_ERR_CODE 0x03 +#define IDE_CMD_CFA_TRANSLATE_SECTOR 0x87 +#define IDE_CMD_CFA_WRITE_MULTIPLE_WO_ERASE 0xCD +#define IDE_CMD_CFA_WRITE_SECTORS_WO_ERASE 0x38 +#define IDE_CMD_CHECK_POWER_MODE1 0xE5 +#define IDE_CMD_CHECK_POWER_MODE2 0x98 +#define IDE_CMD_DEVICE_RESET 0x08 +#define IDE_CMD_EXECUTE_DEVICE_DIAGNOSTIC 0x90 +#define IDE_CMD_FLUSH_CACHE 0xE7 +#define IDE_CMD_FORMAT_TRACK 0x50 +#define IDE_CMD_IDENTIFY_DEVICE 0xEC +#define IDE_CMD_IDENTIFY_DEVICE_PACKET 0xA1 +#define IDE_CMD_IDENTIFY_PACKET_DEVICE 0xA1 +#define IDE_CMD_IDLE1 0xE3 +#define IDE_CMD_IDLE2 0x97 +#define IDE_CMD_IDLE_IMMEDIATE1 0xE1 +#define IDE_CMD_IDLE_IMMEDIATE2 0x95 +#define IDE_CMD_INITIALIZE_DRIVE_PARAMETERS 0x91 +#define IDE_CMD_INITIALIZE_DEVICE_PARAMETERS 0x91 +#define IDE_CMD_NOP 0x00 +#define IDE_CMD_PACKET 0xA0 +#define IDE_CMD_READ_BUFFER 0xE4 +#define IDE_CMD_READ_DMA 0xC8 +#define IDE_CMD_READ_DMA_QUEUED 0xC7 +#define IDE_CMD_READ_MULTIPLE 0xC4 +#define IDE_CMD_READ_SECTORS 0x20 +#define IDE_CMD_READ_SECTORS_EXT 0x24 +#define IDE_CMD_READ_VERIFY_SECTORS 0x40 +#define IDE_CMD_RECALIBRATE 0x10 +#define IDE_CMD_SEEK 0x70 +#define IDE_CMD_SET_FEATURES 0xEF +#define IDE_CMD_SET_MAX_ADDR_EXT 0x24 +#define IDE_CMD_SET_MULTIPLE_MODE 0xC6 +#define IDE_CMD_SLEEP1 0xE6 +#define IDE_CMD_SLEEP2 0x99 +#define IDE_CMD_STANDBY1 0xE2 +#define IDE_CMD_STANDBY2 0x96 +#define IDE_CMD_STANDBY_IMMEDIATE1 0xE0 +#define IDE_CMD_STANDBY_IMMEDIATE2 0x94 +#define IDE_CMD_WRITE_BUFFER 0xE8 +#define IDE_CMD_WRITE_DMA 0xCA +#define IDE_CMD_WRITE_DMA_QUEUED 0xCC +#define IDE_CMD_WRITE_MULTIPLE 0xC5 +#define IDE_CMD_WRITE_SECTORS 0x30 +#define IDE_CMD_WRITE_VERIFY 0x3C + +/* IDE_CMD_SET_FEATURE sub commands */ +#define IDE_FEATURE_CFA_ENABLE_8BIT_PIO 0x01 +#define IDE_FEATURE_ENABLE_WRITE_CACHE 0x02 +#define IDE_FEATURE_SET_TRANSFER_MODE 0x03 +#define IDE_FEATURE_ENABLE_POWER_MANAGEMENT 0x05 +#define IDE_FEATURE_ENABLE_POWERUP_IN_STANDBY 0x06 +#define IDE_FEATURE_STANDBY_SPINUP_DRIVE 0x07 +#define IDE_FEATURE_CFA_ENABLE_POWER_MODE1 0x0A +#define IDE_FEATURE_DISABLE_MEDIA_STATUS_NOTIFICATION 0x31 +#define IDE_FEATURE_ENABLE_AUTOMATIC_ACOUSTIC_MANAGEMENT 0x42 +#define IDE_FEATURE_SET_MAXIMUM_HOST_INTERFACE_SECTOR_TIMES 0x43 +#define IDE_FEATURE_DISABLE_READ_LOOKAHEAD 0x55 +#define IDE_FEATURE_ENABLE_RELEASE_INTERRUPT 0x5D +#define IDE_FEATURE_ENABLE_SERVICE_INTERRUPT 0x5E +#define IDE_FEATURE_DISABLE_REVERTING_TO_POWERON_DEFAULTS 0x66 +#define IDE_FEATURE_CFA_DISABLE_8BIT_PIO 0x81 +#define IDE_FEATURE_DISABLE_WRITE_CACHE 0x82 +#define IDE_FEATURE_DISABLE_POWER_MANAGEMENT 0x85 +#define IDE_FEATURE_DISABLE_POWERUP_IN_STANDBY 0x86 +#define IDE_FEATURE_CFA_DISABLE_POWER_MODE1 0x8A +#define IDE_FEATURE_ENABLE_MEDIA_STATUS_NOTIFICATION 0x95 +#define IDE_FEATURE_ENABLE_READ_LOOKAHEAD 0xAA +#define IDE_FEATURE_DISABLE_AUTOMATIC_ACOUSTIC_MANAGEMENT 0xC2 +#define IDE_FEATURE_ENABLE_REVERTING_TO_POWERON_DEFAULTS 0xCC +#define IDE_FEATURE_DISABLE_SERVICE_INTERRUPT 0xDE + + + +struct controller controller; +struct harddisk_info harddisk_info[2]; + +static int await_ide(int (*done)(struct controller *ctrl), + struct controller *ctrl, unsigned long timeout) +{ + int result; + for(;;) { + result = done(ctrl); + if (result) { + return 0; + } + poll_interruptions(); + if ((timeout == 0) || (currticks() > timeout)) { + break; + } + } + return -1; +} + +/* The maximum time any IDE command can last 31 seconds, + * So if any IDE commands takes this long we know we have problems. + */ +#define IDE_TIMEOUT (32*TICKS_PER_SEC) + +static int not_bsy(struct controller *ctrl) +{ + return !(inb(IDE_REG_STATUS(ctrl)) & IDE_STATUS_BSY); +} +#if !BSY_SET_DURING_SPINUP +static int timeout(struct controller *ctrl) +{ + return 0; +} +#endif + +static int ide_software_reset(struct controller *ctrl) +{ + /* Wait a little bit in case this is immediately after + * hardware reset. + */ + mdelay(2); + /* A software reset should not be delivered while the bsy bit + * is set. If the bsy bit does not clear in a reasonable + * amount of time give up. + */ + if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } + + /* Disable Interrupts and reset the ide bus */ + outb(IDE_CTRL_HD15 | IDE_CTRL_SRST | IDE_CTRL_NIEN, + IDE_REG_DEVICE_CONTROL(ctrl)); + udelay(5); + outb(IDE_CTRL_HD15 | IDE_CTRL_NIEN, IDE_REG_DEVICE_CONTROL(ctrl)); + mdelay(2); + if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } + return 0; +} + +static void pio_set_registers( + struct controller *ctrl, const struct ide_pio_command *cmd) +{ + uint8_t device; + /* Disable Interrupts */ + outb(IDE_CTRL_HD15 | IDE_CTRL_NIEN, IDE_REG_DEVICE_CONTROL(ctrl)); + + /* Possibly switch selected device */ + device = inb(IDE_REG_DEVICE(ctrl)); + outb(cmd->device, IDE_REG_DEVICE(ctrl)); + if ((device & (1UL << 4)) != (cmd->device & (1UL << 4))) { + /* Allow time for the selected drive to switch, + * The linux ide code suggests 50ms is the right + * amount of time to use here. + */ + mdelay(50); + } + outb(cmd->feature, IDE_REG_FEATURE(ctrl)); + outb(cmd->sector_count2, IDE_REG_SECTOR_COUNT(ctrl)); + outb(cmd->sector_count, IDE_REG_SECTOR_COUNT(ctrl)); + outb(cmd->lba_low2, IDE_REG_LBA_LOW(ctrl)); + outb(cmd->lba_low, IDE_REG_LBA_LOW(ctrl)); + outb(cmd->lba_mid2, IDE_REG_LBA_MID(ctrl)); + outb(cmd->lba_mid, IDE_REG_LBA_MID(ctrl)); + outb(cmd->lba_high2, IDE_REG_LBA_HIGH(ctrl)); + outb(cmd->lba_high, IDE_REG_LBA_HIGH(ctrl)); + outb(cmd->command, IDE_REG_COMMAND(ctrl)); +} + + +static int pio_non_data(struct controller *ctrl, const struct ide_pio_command *cmd) +{ + /* Wait until the busy bit is clear */ + if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } + + pio_set_registers(ctrl, cmd); + if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } + /* FIXME is there more error checking I could do here? */ + return 0; +} + +static int pio_data_in(struct controller *ctrl, const struct ide_pio_command *cmd, + void *buffer, size_t bytes) +{ + unsigned int status; + + /* FIXME handle commands with multiple blocks */ + /* Wait until the busy bit is clear */ + if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } + + /* How do I tell if INTRQ is asserted? */ + pio_set_registers(ctrl, cmd); + if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } + status = inb(IDE_REG_STATUS(ctrl)); + if (!(status & IDE_STATUS_DRQ)) { + return -1; + } + insw(IDE_REG_DATA(ctrl), buffer, bytes/2); + status = inb(IDE_REG_STATUS(ctrl)); + if (status & IDE_STATUS_DRQ) { + return -1; + } + return 0; +} + +#if 0 +static int pio_packet(struct controller *ctrl, int in, + const void *packet, int packet_len, + void *buffer, int buffer_len) +{ + const uint8_t *pbuf; + unsigned int status; + struct ide_pio_command cmd; + memset(&cmd, 0, sizeof(cmd)); + + /* Wait until the busy bit is clear */ + if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } + pio_set_registers(ctrl, cmd); + ndelay(400); + if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } + status = inb(IDE_REG_STATUS(ctrl)); + if (!(status & IDE_STATUS_DRQ)) { + return -1; + } + while(packet_len > 1) { + outb(*pbuf, IDE_REG_DATA(ctrl)); + pbuf++; + packet_len -= 1; + } + inb(IDE_REG_ALTSTATUS(ctrl)); + if (await_ide){} + /*FIXME finish this function */ + + +} +#endif + +static inline int ide_read_sector_chs( + struct harddisk_info *info, void *buffer, unsigned long sector) +{ + struct ide_pio_command cmd; + unsigned int track; + unsigned int offset; + unsigned int cylinder; + + memset(&cmd, 0, sizeof(cmd)); + cmd.sector_count = 1; + + track = sector / info->sectors_per_track; + /* Sector number */ + offset = 1 + (sector % info->sectors_per_track); + cylinder = track / info->heads; + cmd.lba_low = offset; + cmd.lba_mid = cylinder & 0xff; + cmd.lba_high = (cylinder >> 8) & 0xff; + cmd.device = IDE_DH_DEFAULT | + IDE_DH_HEAD(track % info->heads) | + info->slave | + IDE_DH_CHS; + cmd.command = IDE_CMD_READ_SECTORS; + return pio_data_in(info->ctrl, &cmd, buffer, IDE_SECTOR_SIZE); +} + +static inline int ide_read_sector_lba( + struct harddisk_info *info, void *buffer, unsigned long sector) +{ + struct ide_pio_command cmd; + memset(&cmd, 0, sizeof(cmd)); + + cmd.sector_count = 1; + cmd.lba_low = sector & 0xff; + cmd.lba_mid = (sector >> 8) & 0xff; + cmd.lba_high = (sector >> 16) & 0xff; + cmd.device = IDE_DH_DEFAULT | + ((sector >> 24) & 0x0f) | + info->slave | + IDE_DH_LBA; + cmd.command = IDE_CMD_READ_SECTORS; + return pio_data_in(info->ctrl, &cmd, buffer, IDE_SECTOR_SIZE); +} + +static inline int ide_read_sector_lba48( + struct harddisk_info *info, void *buffer, sector_t sector) +{ + struct ide_pio_command cmd; + memset(&cmd, 0, sizeof(cmd)); + + cmd.sector_count = 1; + cmd.lba_low = sector & 0xff; + cmd.lba_mid = (sector >> 8) & 0xff; + cmd.lba_high = (sector >> 16) & 0xff; + cmd.lba_low2 = (sector >> 24) & 0xff; + cmd.lba_mid2 = (sector >> 32) & 0xff; + cmd.lba_high2 = (sector >> 40) & 0xff; + cmd.device = info->slave | IDE_DH_LBA; + cmd.command = IDE_CMD_READ_SECTORS_EXT; + return pio_data_in(info->ctrl, &cmd, buffer, IDE_SECTOR_SIZE); +} + + +static int ide_read(struct disk *disk, sector_t sector) +{ + struct harddisk_info *info = disk->priv; + int result; + + /* Report the buffer is empty */ + disk->sector = 0; + disk->bytes = 0; + if (sector > info->sectors) { + return -1; + } + if (info->address_mode == ADDRESS_MODE_CHS) { + result = ide_read_sector_chs(info, disk->buffer, sector); + } + else if (info->address_mode == ADDRESS_MODE_LBA) { + result = ide_read_sector_lba(info, disk->buffer, sector); + } + else if (info->address_mode == ADDRESS_MODE_LBA48) { + result = ide_read_sector_lba48(info, disk->buffer, sector); + } + else { + result = -1; + } + /* On success report the buffer has data */ + if (result != -1) { + disk->bytes = IDE_SECTOR_SIZE; + disk->sector = sector; + } + return result; +} + +static int init_drive(struct harddisk_info *info, struct controller *ctrl, int slave, + int basedrive, unsigned char *buffer) +{ + uint16_t* drive_info; + struct ide_pio_command cmd; + int i; + + info->ctrl = ctrl; + info->heads = 0u; + info->cylinders = 0u; + info->sectors_per_track = 0u; + info->address_mode = IDE_DH_CHS; + info->sectors = 0ul; + info->drive_exists = 0; + info->slave_absent = 0; + info->slave = slave?IDE_DH_SLAVE: IDE_DH_MASTER; + info->basedrive = basedrive; + +#if 0 + printf("Testing for disk %d\n", info->basedrive); +#endif + + /* Select the drive that we are testing */ + outb(IDE_DH_DEFAULT | IDE_DH_HEAD(0) | IDE_DH_CHS | info->slave, + IDE_REG_DEVICE(ctrl)); + mdelay(50); + + /* Test to see if the drive registers exist, + * In many cases this quickly rules out a missing drive. + */ + for(i = 0; i < 4; i++) { + outb(0xaa + i, (ctrl->cmd_base) + 2 + i); + } + for(i = 0; i < 4; i++) { + if (inb((ctrl->cmd_base) + 2 + i) != 0xaa + i) { + return 1; + } + } + for(i = 0; i < 4; i++) { + outb(0x55 + i, (ctrl->cmd_base) + 2 + i); + } + for(i = 0; i < 4; i++) { + if (inb((ctrl->cmd_base) + 2 + i) != 0x55 + i) { + return 1; + } + } +#if 0 + printf("Probing for disk %d\n", info->basedrive); +#endif + + memset(&cmd, 0, sizeof(cmd)); + cmd.device = IDE_DH_DEFAULT | IDE_DH_HEAD(0) | IDE_DH_CHS | info->slave; + cmd.command = IDE_CMD_IDENTIFY_DEVICE; + + + if (pio_data_in(ctrl, &cmd, buffer, IDE_SECTOR_SIZE) < 0) { + /* Well, if that command didn't work, we probably don't have drive. */ + return 1; + } + + + /* Now suck the data out */ + drive_info = (uint16_t *)buffer; + if (drive_info[2] == 0x37C8) { + /* If the response is incomplete spin up the drive... */ + memset(&cmd, 0, sizeof(cmd)); + cmd.device = IDE_DH_DEFAULT | IDE_DH_HEAD(0) | IDE_DH_CHS | + info->slave; + cmd.feature = IDE_FEATURE_STANDBY_SPINUP_DRIVE; + if (pio_non_data(ctrl, &cmd) < 0) { + /* If the command doesn't work give up on the drive */ + return 1; + } + + } + if ((drive_info[2] == 0x37C8) || (drive_info[2] == 0x8C73)) { + /* The response is incomplete retry the drive info command */ + memset(&cmd, 0, sizeof(cmd)); + cmd.device = IDE_DH_DEFAULT | IDE_DH_HEAD(0) | IDE_DH_CHS | + info->slave; + cmd.command = IDE_CMD_IDENTIFY_DEVICE; + if(pio_data_in(ctrl, &cmd, buffer, IDE_SECTOR_SIZE) < 0) { + /* If the command didn't work give up on the drive. */ + return 1; + } + } + if ((drive_info[2] != 0x37C8) && + (drive_info[2] != 0x738C) && + (drive_info[2] != 0x8C73) && + (drive_info[2] != 0xC837) && + (drive_info[2] != 0x0000)) { + printf("Invalid IDE Configuration: %hx\n", drive_info[2]); + return 1; + } + for(i = 27; i < 47; i++) { + info->model_number[((i-27)<< 1)] = (drive_info[i] >> 8) & 0xff; + info->model_number[((i-27)<< 1)+1] = drive_info[i] & 0xff; + } + info->model_number[40] = '\0'; + info->drive_exists = 1; + + /* See if LBA is supported */ + if (drive_info[49] & (1 << 9)) { + info->address_mode = ADDRESS_MODE_LBA; + info->sectors = (drive_info[61] << 16) | (drive_info[60]); + /* Enable LBA48 mode if it is present */ + if (drive_info[83] & (1 <<10)) { + /* Should LBA48 depend on LBA? */ + printf("LBA48 mode\n"); + info->address_mode = ADDRESS_MODE_LBA48; + info->sectors = + (((sector_t)drive_info[103]) << 48) | + (((sector_t)drive_info[102]) << 32) | + (((sector_t)drive_info[101]) << 16) | + (((sector_t)drive_info[100]) << 0); + } + } else { + info->address_mode = ADDRESS_MODE_CHS; + info->heads = drive_info[3]; + info->cylinders = drive_info[1]; + info->sectors_per_track = drive_info[6]; + info->sectors = + info->sectors_per_track * + info->heads * + info->cylinders; + printf( "%s sectors_per_track=[%d], heads=[%d], cylinders=[%d]\n", + __FUNCTION__, + info->sectors_per_track, + info->heads, + info->cylinders); + } + /* See if we have a slave */ + if (!info->slave && (((drive_info[93] >> 14) & 3) == 1)) { + info->slave_absent = !(drive_info[93] & (1 << 5)); + } + /* See if we need to put the device in CFA power mode 1 */ + if ((drive_info[160] & ((1 << 15) | (1 << 13)| (1 << 12))) == + ((1 << 15) | (1 << 13)| (1 << 12))) { + memset(&cmd, 0, sizeof(cmd)); + cmd.device = IDE_DH_DEFAULT | IDE_DH_HEAD(0) | IDE_DH_CHS | info->slave; + cmd.feature = IDE_FEATURE_CFA_ENABLE_POWER_MODE1; + if (pio_non_data(ctrl, &cmd) < 0) { + /* If I need to power up the drive, and I can't + * give up. + */ + printf("Cannot power up CFA device\n"); + return 1; + } + } + printf("disk%d %dk cap: %hx\n", + info->basedrive, + (unsigned long)(info->sectors >> 1), + drive_info[49]); + return 0; +} + +static int init_controller(struct controller *ctrl, int basedrive, unsigned char *buffer) +{ + struct harddisk_info *info; + + /* Intialize the harddisk_info structures */ + memset(harddisk_info, 0, sizeof(harddisk_info)); + + /* Put the drives ide channel in a know state and wait + * for the drives to spinup. + * + * In practice IDE disks tend not to respond to commands until + * they have spun up. This makes IDE hard to deal with + * immediately after power up, as the delays can be quite + * long, so we must be very careful here. + * + * There are two pathological cases that must be dealt with: + * + * - The BSY bit not being set while the IDE drives spin up. + * In this cases only a hard coded delay will work. As + * I have not reproduced it, and this is out of spec for + * IDE drives the work around can be enabled by setting + * BSY_SET_DURING_SPINUP to 0. + * + * - The BSY bit floats high when no drives are plugged in. + * This case will not be detected except by timing out but + * we avoid the problems by only probing devices we are + * supposed to boot from. If we don't do the probe we + * will not experience the problem. + * + * So speed wise I am only slow if the BSY bit is not set + * or not reported by the IDE controller during spinup, which + * is quite rare. + * + */ +#if !BSY_SET_DURING_SPINUP + if (await_ide(timeout, ctrl, IDE_TIMEOUT) < 0) { + return -1; + } +#endif + if (ide_software_reset(ctrl) < 0) { + return -1; + } + + /* Note: I have just done a software reset. It may be + * reasonable to just read the boot time signatures + * off of the drives to see if they are present. + * + * For now I will go with just sending commands to the drives + * and assuming filtering out missing drives by detecting registers + * that won't set and commands that fail to execute properly. + */ + + /* Now initialize the individual drives */ + info = &harddisk_info[0]; + init_drive(info, ctrl, 0, basedrive, buffer); + if (info->drive_exists && !info->slave_absent) { + basedrive++; + info++; + init_drive(info, ctrl, 1, basedrive, buffer); + } + + return 0; +} + +static void ide_disable(struct dev *dev) +{ + struct disk *disk = (struct disk *)dev; + struct harddisk_info *info = disk->priv; + ide_software_reset(info->ctrl); +} + +#ifdef CONFIG_PCI +static int ide_pci_probe(struct dev *dev, struct pci_device *pci) +{ + struct disk *disk = (struct disk *)dev; + struct harddisk_info *info; + int index; + + adjust_pci_device(pci); + + index = dev->index + 1; + if (dev->how_probe == PROBE_NEXT) { + index++; + } + for(; index < 4; index++) { + unsigned mask; + mask = (index < 2)? (1 << 0) : (1 << 2); + if ((pci->class & mask) == 0) { + /* IDE special pci mode */ + uint16_t base; + base = (index < 2)?IDE_BASE0:IDE_BASE1; + controller.cmd_base = base; + controller.ctrl_base = base + IDE_REG_EXTENDED_OFFSET; + } else { + /* IDE normal pci mode */ + unsigned cmd_reg, ctrl_reg; + uint32_t cmd_base, ctrl_base; + if (index < 2) { + cmd_reg = PCI_BASE_ADDRESS_0; + ctrl_reg = PCI_BASE_ADDRESS_1; + } else { + cmd_reg = PCI_BASE_ADDRESS_2; + ctrl_reg = PCI_BASE_ADDRESS_3; + } + pcibios_read_config_dword(pci->bus, pci->devfn, cmd_reg, &cmd_base); + pcibios_read_config_dword(pci->bus, pci->devfn, ctrl_reg, &ctrl_base); + controller.cmd_base = cmd_base & ~3; + controller.ctrl_base = ctrl_base & ~3; + } + if (((index & 1) == 0) || (dev->how_probe == PROBE_AWAKE)) { + if (init_controller(&controller, disk->drive, disk->buffer) < 0) { + /* nothing behind the controller */ + continue; + } + } + info = &harddisk_info[index & 1]; + if (!info->drive_exists) { + /* unknown drive */ + continue; + } + disk->hw_sector_size = IDE_SECTOR_SIZE; + disk->sectors_per_read = 1; + disk->sectors = info->sectors; + dev->index = index; + dev->disable = ide_disable; + disk->read = ide_read; + disk->priv = info; + + return 1; + } + /* past all of the drives */ + dev->index = 0; + return 0; +} +#define PCI_DEVICE_ID_INTEL_82801CA_11 0x248b +static struct pci_id ide_controllers[] = { +{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_11, "PIIX4" }, +#if 0 /* Currently I don't need any entries in this table so ignore it */ +{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371FB_0, "PIIX" }, +{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371FB_1, "PIIX" }, +{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371MX, "MPIIX" }, +{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1, "PIIX3" }, +{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB, "PIIX4" }, +{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_1, "PIIX4" }, +{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_1, "PIIX4" }, +{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_1, "PIIX4" }, +{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82372FB_1, "PIIX4" }, +{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82451NX, "PIIX4" }, +{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_9, "PIIX4" }, +{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_8, "PIIX4" }, +{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_10, "PIIX4" }, +{ PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C561, "VIA_IDE" }, +{ PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C576_1, "VP_IDE" }, +{ PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_1, "VP_IDE" }, +{ PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20246, "PDC20246" }, +{ PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20262, "PDC20262" }, +{ PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20265, "PDC20265" }, +{ PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20267, "PDC20267" }, +{ PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20268, "PDC20268" }, +{ PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20268R, "PDC20268" }, +{ PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000, "RZ1000" }, +{ PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1001, "RZ1001" }, +{ PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_SAMURAI_IDE, "SAMURAI" }, +{ PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_640, "CMD640" }, +{ PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_643, "CMD646" }, +{ PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_646, "CMD648" }, +{ PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_648, "CMD643" }, +{ PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_649, "CMD649" }, +{ PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5513, "SIS5513" }, +{ PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C621, "OPTI621" }, +{ PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C558, "OPTI621V" }, +{ PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C825, "OPTI621X" }, +{ PCI_VENDOR_ID_TEKRAM, PCI_DEVICE_ID_TEKRAM_DC290, "TRM290" }, +{ PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410, "NS87410" }, +{ PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87415, "NS87415" }, +{ PCI_VENDOR_ID_HOLTEK2, PCI_DEVICE_ID_HOLTEK2_6565, "HT6565" }, +{ PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP850UF, "AEC6210" }, +{ PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP860, "AEC6260" }, +{ PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP860R, "AEC6260R" }, +{ PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_82C105, "W82C105" }, +{ PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8673F, "UM8673F" }, +{ PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886A, "UM8886A" }, +{ PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886BF, "UM8886BF" }, +{ PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT343, "HPT34X" }, +{ PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT366, "HPT366" }, +{ PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5229, "ALI15X3" }, +{ PCI_VENDOR_ID_CONTAQ, PCI_DEVICE_ID_CONTAQ_82C693, "CY82C693" }, +{ 0x3388, 0x8013, "HINT_IDE" }, +{ PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_IDE, "CS5530" }, +{ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_COBRA_7401, "AMD7401" }, +{ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7409, "AMD7409" }, +{ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7411, "AMD7411" }, +{ PCI_VENDOR_ID_PDC, PCI_DEVICE_ID_PDC_1841, "PDCADMA" }, +{ PCI_VENDOR_ID_EFAR, PCI_DEVICE_ID_EFAR_SLC90E66_1, "SLC90E66" }, +{ PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4IDE, "OSB4" }, +{ PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, "OSB5" }, +{ PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_IT8172G, "ITE8172G" }, +#endif +}; + +static struct pci_driver ide_driver __pci_driver = { + .type = DISK_DRIVER, + .name = "IDE", + .probe = ide_pci_probe, + .ids = ide_controllers, + .id_count = sizeof(ide_controllers)/sizeof(ide_controllers), + .class = PCI_CLASS_STORAGE_IDE, +}; +#endif + +/* The isa driver works but it causes disks to show up twice. + * comment it out for now. + */ +#if 0 && defined(CONFIG_ISA) +static int ide_isa_probe(struct dev * dev, unsigned short *probe_addrs) +{ + struct disk *disk = (struct disk *)dev; + int index; + unsigned short addr; + struct harddisk_info *info; + + index = dev->index +1; + if (dev->how_probe == PROBE_AWAKE) { + index--; + } + for(; (index >= 0) && (addr = probe_addrs[index >> 1]); index += 2) { + if ((index & 1) == 0) { + controller.cmd_base = addr; + controller.ctrl_base = addr + IDE_REG_EXTENDED_OFFSET; + if (init_controller(&controller, disk->drive, disk->buffer) < 0) { + /* nothing behind the controller */ + continue; + } + } + info = &harddisk_info[index & 1]; + if (!info->drive_exists) { + /* unknown drive */ + return 0; + } + disk->sectors_per_read = 1; + disk->sectors = info->sectors; + dev->index = index; + dev->disable = ide_disable; + disk->read = ide_read; + disk->priv = info; + + return 1; + } + /* past all of the drives */ + dev->index = -1; + return 0; +} + +static unsigned short ide_base[] = { + IDE_BASE0, + IDE_BASE1, + IDE_BASE2, + IDE_BASE3, + 0 +}; +static struct isa_driver ide_isa_driver __isa_driver = { + .type = DISK_DRIVER, + .name = "IDE/ISA", + .probe = ide_isa_probe, + .ioaddrs = ide_base, +}; + +#endif diff --git a/src/drivers/disk/pc_floppy.c b/src/drivers/disk/pc_floppy.c new file mode 100644 index 00000000..7d953a28 --- /dev/null +++ b/src/drivers/disk/pc_floppy.c @@ -0,0 +1,1151 @@ +#include "etherboot.h" +#include "timer.h" +#include "disk.h" +#include "isa.h" + + +#undef MDEBUG + +static int FD_BASE = 0x3f0; + +#define FD_DRIVE 0 + +#define FD_STATUS_A (FD_BASE + 0) /* Status register A */ +#define FD_STATUS_B (FD_BASE + 1) /* Status register B */ +#define FD_DOR (FD_BASE + 2) /* Digital Output Register */ +#define FD_TDR (FD_BASE + 3) /* Tape Drive Register */ +#define FD_STATUS (FD_BASE + 4) /* Main Status Register */ +#define FD_DSR (FD_BASE + 4) /* Data Rate Select Register (old) */ +#define FD_DATA (FD_BASE + 5) /* Data Transfer (FIFO) register */ +#define FD_DIR (FD_BASE + 7) /* Digital Input Register (read) */ +#define FD_DCR (FD_BASE + 7) /* Diskette Control Register (write)*/ + +/* Bit of FD_STATUS_A */ +#define STA_INT_PENDING 0x80 /* Interrupt Pending */ + +/* DOR */ +#define DOR_DRIVE0 0x00 +#define DOR_DRIVE1 0x01 +#define DOR_DRIVE2 0x02 +#define DOR_DRIVE3 0x03 +#define DOR_DRIVE_MASK 0x03 +#define DOR_NO_RESET 0x04 +#define DOR_DMA_EN 0x08 +#define DOR_MOT_EN0 0x10 +#define DOR_MOT_EN1 0x20 +#define DOR_MOT_EN2 0x40 +#define DOR_MOT_EN3 0x80 + +/* Bits of main status register */ +#define STATUS_BUSYMASK 0x0F /* drive busy mask */ +#define STATUS_BUSY 0x10 /* FDC busy */ +#define STATUS_NON_DMA 0x20 /* 0- DMA mode */ +#define STATUS_DIR 0x40 /* 0- cpu->fdc */ +#define STATUS_READY 0x80 /* Data reg ready */ + +/* Bits of FD_ST0 */ +#define ST0_DS 0x03 /* drive select mask */ +#define ST0_HA 0x04 /* Head (Address) */ +#define ST0_NR 0x08 /* Not Ready */ +#define ST0_ECE 0x10 /* Equipment check error */ +#define ST0_SE 0x20 /* Seek end */ +#define ST0_INTR 0xC0 /* Interrupt code mask */ +#define ST0_INTR_OK (0 << 6) +#define ST0_INTR_ERROR (1 << 6) +#define ST0_INTR_INVALID (2 << 6) +#define ST0_INTR_POLL_ERROR (3 << 6) + +/* Bits of FD_ST1 */ +#define ST1_MAM 0x01 /* Missing Address Mark */ +#define ST1_WP 0x02 /* Write Protect */ +#define ST1_ND 0x04 /* No Data - unreadable */ +#define ST1_OR 0x10 /* OverRun */ +#define ST1_CRC 0x20 /* CRC error in data or addr */ +#define ST1_EOC 0x80 /* End Of Cylinder */ + +/* Bits of FD_ST2 */ +#define ST2_MAM 0x01 /* Missing Address Mark (again) */ +#define ST2_BC 0x02 /* Bad Cylinder */ +#define ST2_SNS 0x04 /* Scan Not Satisfied */ +#define ST2_SEH 0x08 /* Scan Equal Hit */ +#define ST2_WC 0x10 /* Wrong Cylinder */ +#define ST2_CRC 0x20 /* CRC error in data field */ +#define ST2_CM 0x40 /* Control Mark = deleted */ + +/* Bits of FD_ST3 */ +#define ST3_HA 0x04 /* Head (Address) */ +#define ST3_DS 0x08 /* drive is double-sided */ +#define ST3_TZ 0x10 /* Track Zero signal (1=track 0) */ +#define ST3_RY 0x20 /* drive is ready */ +#define ST3_WP 0x40 /* Write Protect */ +#define ST3_FT 0x80 /* Drive Fault */ + +/* Values for FD_COMMAND */ +#define FD_RECALIBRATE 0x07 /* move to track 0 */ +#define FD_SEEK 0x0F /* seek track */ +#define FD_READ 0xA6 /* read with MT, SKip deleted */ +#define FD_WRITE 0xC5 /* write with MT, MFM */ +#define FD_SENSEI 0x08 /* Sense Interrupt Status */ +#define FD_SPECIFY 0x03 /* specify HUT etc */ +#define FD_FORMAT 0x4D /* format one track */ +#define FD_VERSION 0x10 /* get version code */ +#define FD_CONFIGURE 0x13 /* configure FIFO operation */ +#define FD_PERPENDICULAR 0x12 /* perpendicular r/w mode */ +#define FD_GETSTATUS 0x04 /* read ST3 */ +#define FD_DUMPREGS 0x0E /* dump the contents of the fdc regs */ +#define FD_READID 0xEA /* prints the header of a sector */ +#define FD_UNLOCK 0x14 /* Fifo config unlock */ +#define FD_LOCK 0x94 /* Fifo config lock */ +#define FD_RSEEK_OUT 0x8f /* seek out (i.e. to lower tracks) */ +#define FD_RSEEK_IN 0xcf /* seek in (i.e. to higher tracks) */ + + +/* the following commands are new in the 82078. They are not used in the + * floppy driver, except the first three. These commands may be useful for apps + * which use the FDRAWCMD interface. For doc, get the 82078 spec sheets at + * http://www-techdoc.intel.com/docs/periph/fd_contr/datasheets/ */ + +#define FD_PARTID 0x18 /* part id ("extended" version cmd) */ +#define FD_SAVE 0x2e /* save fdc regs for later restore */ +#define FD_DRIVESPEC 0x8e /* drive specification: Access to the + * 2 Mbps data transfer rate for tape + * drives */ + +#define FD_RESTORE 0x4e /* later restore */ +#define FD_POWERDOWN 0x27 /* configure FDC's powersave features */ +#define FD_FORMAT_N_WRITE 0xef /* format and write in one go. */ +#define FD_OPTION 0x33 /* ISO format (which is a clean way to + * pack more sectors on a track) */ + +/* FDC version return types */ +#define FDC_NONE 0x00 +#define FDC_UNKNOWN 0x10 /* DO NOT USE THIS TYPE EXCEPT IF IDENTIFICATION + FAILS EARLY */ +#define FDC_8272A 0x20 /* Intel 8272a, NEC 765 */ +#define FDC_765ED 0x30 /* Non-Intel 1MB-compatible FDC, can't detect */ +#define FDC_82072 0x40 /* Intel 82072; 8272a + FIFO + DUMPREGS */ +#define FDC_82072A 0x45 /* 82072A (on Sparcs) */ +#define FDC_82077_ORIG 0x51 /* Original version of 82077AA, sans LOCK */ +#define FDC_82077 0x52 /* 82077AA-1 */ +#define FDC_82078_UNKN 0x5f /* Unknown 82078 variant */ +#define FDC_82078 0x60 /* 44pin 82078 or 64pin 82078SL */ +#define FDC_82078_1 0x61 /* 82078-1 (2Mbps fdc) */ +#define FDC_S82078B 0x62 /* S82078B (first seen on Adaptec AVA-2825 VLB + * SCSI/EIDE/Floppy controller) */ +#define FDC_87306 0x63 /* National Semiconductor PC 87306 */ + +/* + * Beware: the fdc type list is roughly sorted by increasing features. + * Presence of features is tested by comparing the FDC version id with the + * "oldest" version that has the needed feature. + * If during FDC detection, an obscure test fails late in the sequence, don't + * assign FDC_UNKNOWN. Else the FDC will be treated as a dumb 8272a, or worse. + * This is especially true if the tests are unneeded. + */ + +/* Parameters for a 1.44 3.5" disk */ +#define DISK_H1440_SIZE 2880 +#define DISK_H1440_SECT 18 +#define DISK_H1440_HEAD 2 +#define DISK_H1440_TRACK 80 +#define DISK_H1440_STRETCH 0 +#define DISK_H1440_GAP 0x1B +#define DISK_H1440_RATE 0x00 +#define DISK_H1440_SPEC1 0xCF +#define DISK_H1440_FMT_GAP 0x6C + +/* Parameters for a 1.44 3.5" drive */ +#define DRIVE_H1440_MAX_DTR 500 +#define DRIVE_H1440_HLT 16 /* ms */ +#define DRIVE_H1440_HUT 16 /* ms */ +#define DRIVE_H1440_SRT 4000 /* us */ +#define DRIVE_H1440_SPINUP 400 /* ms */ +#define DRIVE_H1440_SPINDOWN 3000 /* ms */ +#define DRIVE_H1440_SPINDOWN_OFFSET 10 +#define DRIVE_H1440_SELECT_DELAY 20 /* ms */ +#define DRIVE_H1440_RPS 5 +#define DRIVE_H1440_TRACKS 83 +#define DRIVE_H1440_TIMEOUT 3000 /* ms */ +#define DRIVE_H1440_INTERLEAVE_SECT 20 + +/* Floppy drive configuration */ +#define FIFO_DEPTH 10 +#define USE_IMPLIED_SEEK 0 +#define USE_FIFO 1 +#define FIFO_THRESHOLD 10 +#define TRACK_PRECOMPENSATION 0 + +#define SLOW_FLOPPY 0 + +#define FD_RESET_DELAY 20 /* microseconds */ + +/* + * FDC state + */ +struct drive_state { + unsigned track; +} drive_state[1]; + +struct floppy_fdc_state { + int in_sync; + int spec1; /* spec1 value last used */ + int spec2; /* spec2 value last used */ + unsigned dtr; + unsigned char dor; + unsigned char version; /* FDC version code */ +} fdc_state; + + + +/* Synchronization of FDC access. */ +#define FD_COMMAND_NONE -1 +#define FD_COMMAND_ERROR 2 +#define FD_COMMAND_OKAY 3 + +/* + * globals used by 'result()' + */ +#define MAX_REPLIES 16 + +static void show_floppy(void); +static void floppy_reset(void); + + +static int set_dor(char mask, char data) +{ + unsigned char newdor,olddor; + + olddor = fdc_state.dor; + newdor = (olddor & mask) | data; + if (newdor != olddor){ + fdc_state.dor = newdor; + outb(newdor, FD_DOR); + } + return olddor; +} + + +static void bounce_motor(void) +{ + /* Bounce the drive motor... */ + outb(fdc_state.dor & ~(0x10<> 2) & 1; + int status; + unsigned new_dor; + if (drive > 3) { + printf("bad drive value\n"); + return; + } + if (fdc != 0) { + printf("bad fdc value\n"); + return; + } + drive &= 3; +#if 0 + new_dor = 8; /* Enable the controller */ +#else + new_dor = 0; /* Don't enable DMA on the controller */ +#endif + new_dor |= (1 << (drive + 4)); /* Spinup the selected drive */ + new_dor |= drive; /* Select the drive for commands as well */ + set_dor(0xc, new_dor); + + mdelay(DRIVE_H1440_SPINUP); + + status = inb(FD_STATUS); +#ifdef MDEBUG + printf("set_drive status = %hx, new_dor = %hx\n", status, new_dor); +#endif + if (status != STATUS_READY) { + printf("set_drive bad status\n"); + } +} + + +/* Disable the motor for a given floppy drive */ +static void floppy_motor_off(int drive) +{ + unsigned mask; +#ifdef MDEBUG + printf("floppy_motor_off\n"); +#endif + /* fix the number of drives */ + drive &= 3; + /* Clear the bit for the drive we care about */ + mask = 0xff; + mask &= ~(1 << (drive +4)); + /* Now clear the bit in the Digital Output Register */ + set_dor(mask, 0); +} + +/* Set the FDC's data transfer rate on behalf of the specified drive. + * NOTE: with 82072/82077 FDCs, changing the data rate requires a reissue + * of the specify command (i.e. using the fdc_specify function). + */ +static void fdc_dtr(unsigned rate) +{ + rate &= 3; + /* If data rate not already set to desired value, set it. */ + if (fdc_state.in_sync && (rate == fdc_state.dtr)) + return; + + /* Set dtr */ + outb(rate, FD_DCR); + + /* TODO: some FDC/drive combinations (C&T 82C711 with TEAC 1.2MB) + * need a stabilization period of several milliseconds to be + * enforced after data rate changes before R/W operations. + * Pause 5 msec to avoid trouble. (Needs to be 2 jiffies) + */ + fdc_state.dtr = rate & 3; + mdelay(5); +} /* fdc_dtr */ + +static int fdc_configure(int use_implied_seek, int use_fifo, + unsigned fifo_threshold, unsigned precompensation) +{ + unsigned config_bits; + unsigned char cmd[4]; + /* 0 EIS EFIFO POLL FIFOOTHR[4] */ + + /* santize parameters */ + config_bits = fifo_threshold & 0xf; + config_bits |= (1 << 4); /* Always disable background floppy poll */ + config_bits |= (!use_fifo) << 5; + config_bits |= (!!use_implied_seek) << 6; + + precompensation &= 0xff; /* pre-compensation from track 0 upwards */ + + cmd[0] = FD_CONFIGURE; + cmd[1] = 0; + cmd[2] = config_bits; + cmd[3] = precompensation; + + /* Turn on FIFO */ + if (output_new_command(cmd, 4) < 0) + return 0; + return 1; +} + +#define NOMINAL_DTR 500 +/* Issue a "SPECIFY" command to set the step rate time, head unload time, + * head load time, and DMA disable flag to values needed by floppy. + * + * The value "dtr" is the data transfer rate in Kbps. It is needed + * to account for the data rate-based scaling done by the 82072 and 82077 + * FDC types. This parameter is ignored for other types of FDCs (i.e. + * 8272a). + * + * Note that changing the data transfer rate has a (probably deleterious) + * effect on the parameters subject to scaling for 82072/82077 FDCs, so + * fdc_specify is called again after each data transfer rate + * change. + * + * srt: 1000 to 16000 in microseconds + * hut: 16 to 240 milliseconds + * hlt: 2 to 254 milliseconds + * + * These values are rounded up to the next highest available delay time. + */ +static void fdc_specify( + unsigned head_load_time, unsigned head_unload_time, unsigned step_rate) +{ + unsigned char cmd[3]; + unsigned long srt, hlt, hut; + unsigned long dtr = NOMINAL_DTR; + unsigned long scale_dtr = NOMINAL_DTR; + int hlt_max_code = 0x7f; + int hut_max_code = 0xf; + +#ifdef MDEBUG + printf("fdc_specify\n"); +#endif + + switch (DISK_H1440_RATE & 0x03) { + case 3: + dtr = 1000; + break; + case 1: + dtr = 300; + if (fdc_state.version >= FDC_82078) { + unsigned char cmd[3]; + /* chose the default rate table, not the one + * where 1 = 2 Mbps */ + cmd[0] = FD_DRIVESPEC; + cmd[1] = FD_DRIVE & 3; + cmd[2] = 0xc0; + output_new_command(cmd,3); + /* FIXME how do I handle errors here? */ + } + break; + case 2: + dtr = 250; + break; + } + + + if (fdc_state.version >= FDC_82072) { + scale_dtr = dtr; + hlt_max_code = 0x00; /* 0==256msec*dtr0/dtr (not linear!) */ + hut_max_code = 0x0; /* 0==256msec*dtr0/dtr (not linear!) */ + } + + /* Convert step rate from microseconds to milliseconds and 4 bits */ + srt = 16 - (step_rate*scale_dtr/1000 + NOMINAL_DTR - 1)/NOMINAL_DTR; + if (SLOW_FLOPPY) { + srt = srt / 4; + } + if (srt > 0xf) { + srt = 0xf; + } + if (srt < 0 ) { + srt = 0; + } + + hlt = (head_load_time*scale_dtr/2 + NOMINAL_DTR - 1)/NOMINAL_DTR; + if (hlt < 0x01) + hlt = 0x01; + else if (hlt > 0x7f) + hlt = hlt_max_code; + + hut = (head_unload_time*scale_dtr/16 + NOMINAL_DTR - 1)/NOMINAL_DTR; + if (hut < 0x1) + hut = 0x1; + else if (hut > 0xf) + hut = hut_max_code; + + cmd[0] = FD_SPECIFY; + cmd[1] = (srt << 4) | hut; + cmd[2] = (hlt << 1) | 1; /* Always disable DMA */ + + /* If these parameters did not change, just return with success */ + if (!fdc_state.in_sync || fdc_state.spec1 != cmd[1] || fdc_state.spec2 != cmd[2]) { + /* Go ahead and set spec1 and spec2 */ + output_command(cmd, 3); + /* FIXME how do I handle errors here... */ +#ifdef MDEBUG + printf("FD_SPECIFY(%hx, %hx)\n", cmd[1], cmd[2]); +#endif + } +} /* fdc_specify */ + + +/* + * reset is done by pulling bit 2 of DOR low for a while (old FDCs), + * or by setting the self clearing bit 7 of STATUS (newer FDCs) + */ +static void reset_fdc(void) +{ + unsigned char reply[MAX_REPLIES]; + + fdc_state.in_sync = 0; + + /* Pseudo-DMA may intercept 'reset finished' interrupt. */ + /* Irrelevant for systems with true DMA (i386). */ + + if (fdc_state.version >= FDC_82072A) + outb(0x80 | (fdc_state.dtr &3), FD_DSR); + else { + outb(fdc_state.dor & ~DOR_NO_RESET, FD_DOR); + udelay(FD_RESET_DELAY); + outb(fdc_state.dor, FD_DOR); + } + result(reply, MAX_REPLIES); +} + + + +static void show_floppy(void) +{ + +#ifdef MDEBUG + printf("\n"); + printf("floppy driver state\n"); + printf("-------------------\n"); + + printf("fdc_bytes: %hx %hx xx %hx %hx %hx xx %hx\n", + inb(FD_BASE + 0), inb(FD_BASE + 1), + inb(FD_BASE + 3), inb(FD_BASE + 4), inb(FD_BASE + 5), + inb(FD_BASE + 7)); + + printf("status=%x\n", inb(FD_STATUS)); + printf("\n"); +#endif +} + +static void floppy_recalibrate(void) +{ + unsigned char cmd[2]; + unsigned char reply[MAX_REPLIES]; + int nr, success; + success = 0; + do { +#ifdef MDEBUG + printf("floppy_recalibrate\n"); +#endif + /* Send the recalibrate command to the controller. + * We don't have interrupts or anything we can poll + * so we have to guess when it is done. + */ + cmd[0] = FD_RECALIBRATE; + cmd[1] = 0; + if (output_command(cmd, 2) < 0) + continue; + + /* Sleep for the maximum time the recalibrate command + * can run. + */ + mdelay(80*DRIVE_H1440_SRT/1000); + + /* Now call FD_SENSEI to end the command + * and collect up the reply. + */ + if (output_byte(FD_SENSEI) < 0) + continue; + nr = result(reply, MAX_REPLIES); + + /* Now see if we have succeeded in our seek */ + success = + /* We have the right size result */ + (nr == 2) && + /* The command didn't terminate in error */ + ((reply[0] & ST0_INTR) == ST0_INTR_OK) && + /* We finished a seek */ + (reply[0] & ST0_SE) && + /* We are at cylinder 0 */ + (reply[1] == 0); + } while(!success); + /* Remember we are at track 0 */ + drive_state[FD_DRIVE].track = 0; +} + + +static int __floppy_seek(unsigned track) +{ + unsigned char cmd[3]; + unsigned char reply[MAX_REPLIES]; + int nr, success; + unsigned distance, old_track; + + /* Look up the old track and see if we need to + * do anything. + */ + old_track = drive_state[FD_DRIVE].track; + if (old_track == track) { + return 1; + } + + /* Compute the distance we are about to move, + * We need to know this so we know how long to sleep... + */ + distance = (old_track > track)?(old_track - track):(track - old_track); + distance += 1; + + + /* Send the seek command to the controller. + * We don't have interrupts or anything we can poll + * so we have to guess when it is done. + */ + cmd[0] = FD_SEEK; + cmd[1] = FD_DRIVE; + cmd[2] = track; + if (output_command(cmd, 3) < 0) + return 0; + + /* Sleep for the time it takes to step through distance tracks. + */ + mdelay(distance*DRIVE_H1440_SRT/1000); + + /* Now call FD_SENSEI to end the command + * and collect up the reply. + */ + cmd[0] = FD_SENSEI; + if (output_command(cmd, 1) < 0) + return 0; + nr = result(reply, MAX_REPLIES); + + /* Now see if we have succeeded in our seek */ + success = + /* We have the right size result */ + (nr == 2) && + /* The command didn't terminate in error */ + ((reply[0] & ST0_INTR) == ST0_INTR_OK) && + /* We finished a seek */ + (reply[0] & ST0_SE) && + /* We are at cylinder 0 */ + (reply[1] == track); + if (success) + drive_state[FD_DRIVE].track = track; + else { +#ifdef MDEBUG + printf("seek failed\n"); + printf("nr = %d\n", nr); + printf("ST0 = %hx\n", reply[0]); + printf("PCN = %hx\n", reply[1]); + printf("status = %d\n", inb(FD_STATUS)); +#endif + } + return success; +} + +static int floppy_seek(unsigned track) +{ + unsigned old_track; + int result; + /* assume success */ + result = 1; + + /* Look up the old track and see if we need to + * do anything. + */ + old_track = drive_state[FD_DRIVE].track; + if (old_track == track) { + return result; + } + /* For some reason seeking many tracks at once is + * problematic so only seek a single track at a time. + */ + while(result && (old_track > track)) { + old_track--; + result = __floppy_seek(old_track); + } + while(result && (track > old_track)) { + old_track++; + result = __floppy_seek(old_track); + } + return result; +} + +static int read_ok(unsigned head) +{ + unsigned char results[7]; + int result_ok; + int nr; + + /* read back the read results */ + nr = result(results, 7); + + /* Now see if they say we are o.k. */ + result_ok = 0; + /* Are my result bytes o.k.? */ + if (nr == 7) { + /* Are we o.k. */ + if ((results[0] & ST0_INTR) == ST0_INTR_OK) { + result_ok = 1; + } + /* Or did we get just an overflow error */ + else if (((results[0] & ST0_INTR) == ST0_INTR_ERROR) && + (results[1]== ST1_OR) && + (results[2] == 0)) { + result_ok = 1; + } + /* Verify the reply had the correct head */ + if (((results[0] & ST0_HA) >> 2) != head) { + result_ok = 0; + } + /* Verify the reply had the correct drive */ + if (((results[0] & ST0_DS) != FD_DRIVE)) { + result_ok = 0; + } + } + if (!result_ok) { +#ifdef MDEBUG + printf("result_bytes = %d\n", nr); + printf("ST0 = %hx\n", results[0]); + printf("ST1 = %hx\n", results[1]); + printf("ST2 = %hx\n", results[2]); + printf(" C = %hx\n", results[3]); + printf(" H = %hx\n", results[4]); + printf(" R = %hx\n", results[5]); + printf(" N = %hx\n", results[6]); +#endif + } + return result_ok; +} + +static int floppy_read_sectors( + char *dest, unsigned byte_offset, unsigned length, + unsigned sector, unsigned head, unsigned track) +{ + /* MT == Multitrack */ + /* MFM == MFM or FM Mode */ + /* SK == Skip deleted data addres Mark */ + /* HDS == Head number select */ + /* DS0 == Disk Drive Select 0 */ + /* DS1 == Disk Drive Select 1 */ + /* C == Cylinder number 0 - 255 */ + /* H == Head number */ + /* R == Record */ + /* N == The number of data bytes written in a sector */ + /* EOT == End of Track */ + /* GPL == Gap Length */ + /* DTL == Data Length */ + /* MT MFM SK 0 1 1 0 0 */ + /* 0 0 0 0 0 HDS DS1 DS0 */ + /* C, H, R, N, EOT, GPL, DTL */ + + int i, status, result_ok; + int max_bytes, bytes_read; + int ret; + unsigned char cmd[9]; + unsigned end_offset; + + end_offset = byte_offset + length; + max_bytes = 512*(DISK_H1440_SECT - sector + 1); + + if (byte_offset >= max_bytes) { + return 0; + } + cmd[0] = FD_READ | (((DISK_H1440_HEAD ==2)?1:0) << 6); + cmd[1] = (head << 2) | FD_DRIVE; + cmd[2] = track; + cmd[3] = head; + cmd[4] = sector; + cmd[5] = 2; /* 2^N *128 == Sector size. Hard coded to 512 bytes */ + cmd[6] = DISK_H1440_SECT; + cmd[7] = DISK_H1440_GAP; + cmd[8] = 0xff; + + /* Output the command bytes */ + if (output_command(cmd, 9) < 0) + return -1; + + /* The execution stage begins when STATUS_READY&STATUS_NON_DMA is set */ + do { +#if 1 + poll_interruptions(); +#endif + status = inb(FD_STATUS); + status &= STATUS_READY | STATUS_NON_DMA; + } while(status != (STATUS_READY|STATUS_NON_DMA)); + + for(i = 0; i < max_bytes; i++) { + unsigned char byte; + if ((status = wait_til_ready()) < 0) { + break; + } + status &= STATUS_READY|STATUS_DIR|STATUS_NON_DMA; + if (status != (STATUS_READY|STATUS_DIR|STATUS_NON_DMA)) { + break; + } + byte = inb(FD_DATA); + if ((i >= byte_offset) && (i < end_offset)) { + dest[i - byte_offset] = byte; + } + } + bytes_read = i; + + /* The result stage begins when STATUS_NON_DMA is cleared */ + while((status = inb(FD_STATUS)) & STATUS_NON_DMA) { + /* We get extra bytes in the fifo past + * the end of the sector and drop them on the floor. + * Otherwise the fifo is polluted. + */ + inb(FD_DATA); + } + /* Did I get an error? */ + result_ok = read_ok(head); + /* Did I read enough bytes? */ + ret = -1; + if (result_ok && (bytes_read == max_bytes)) { + ret = bytes_read - byte_offset; + if (ret > length) { + ret = length; + } + } + + if (ret < 0) { +#ifdef MDEBUG + printf("ret = %d\n", ret); + printf("bytes_read = %d\n", bytes_read); + printf("status = %x\n", status); +#endif + } + return ret; +} + +static int floppy_read(struct disk *disk, sector_t base_sector) +{ + unsigned head, track, sector, byte_offset; + unsigned long block; + int ret; + + disk->sector = 0; + disk->bytes = 0; + + block = base_sector; + block /= disk->sectors_per_read; + + /* break the offset up into sectors and bytes */ + byte_offset = 0; + + /* Find the disk block we are starting with... */ + sector = 1; + head = block % DISK_H1440_HEAD; + track = (block / DISK_H1440_HEAD)% DISK_H1440_TRACK; + + /* First seek to our start track */ + if (!floppy_seek(track)) { + return -1; + } + /* Then read the data */ + ret = floppy_read_sectors( + disk->buffer, byte_offset, SECTOR_SIZE*disk->sectors_per_read, sector, head, track); + if (ret >= 0) { + disk->sector = block * disk->sectors_per_read; + disk->bytes = SECTOR_SIZE * disk->sectors_per_read; + return ret; + } + /* If we failed reset the fdc... */ + floppy_reset(); + return -1; +} + +/* Determine the floppy disk controller type */ +/* This routine was written by David C. Niemi */ +static char get_fdc_version(void) +{ + int bytes, ret; + unsigned char reply_buffer[MAX_REPLIES]; + + ret = output_byte(FD_DUMPREGS); /* 82072 and better know DUMPREGS */ + if (ret < 0) + return FDC_NONE; + if ((bytes = result(reply_buffer, MAX_REPLIES)) <= 0x00) + return FDC_NONE; /* No FDC present ??? */ + if ((bytes==1) && (reply_buffer[0] == 0x80)){ + printf("FDC %d is an 8272A\n"); + return FDC_8272A; /* 8272a/765 don't know DUMPREGS */ + } + if (bytes != 10) { +#ifdef MDEBUG + printf("init: DUMPREGS: unexpected return of %d bytes.\n", + bytes); +#endif + return FDC_UNKNOWN; + } + if (!fdc_configure(USE_IMPLIED_SEEK, USE_FIFO, FIFO_THRESHOLD, + TRACK_PRECOMPENSATION)) { + printf("FDC is an 82072\n"); + return FDC_82072; /* 82072 doesn't know CONFIGURE */ + } + + output_byte(FD_PERPENDICULAR); + if (need_more_output() == MORE_OUTPUT) { + output_byte(0); + } else { + printf("FDC is an 82072A\n"); + return FDC_82072A; /* 82072A as found on Sparcs. */ + } + + output_byte(FD_UNLOCK); + bytes = result(reply_buffer, MAX_REPLIES); + if ((bytes == 1) && (reply_buffer[0] == 0x80)){ + printf("FDC is a pre-1991 82077\n"); + return FDC_82077_ORIG; /* Pre-1991 82077, doesn't know + * LOCK/UNLOCK */ + } + if ((bytes != 1) || (reply_buffer[0] != 0x00)) { +#ifdef MDEBUG + printf("FDC init: UNLOCK: unexpected return of %d bytes.\n", + bytes); +#endif + return FDC_UNKNOWN; + } + output_byte(FD_PARTID); + bytes = result(reply_buffer, MAX_REPLIES); + if (bytes != 1) { +#ifdef MDEBUG + printf("FDC init: PARTID: unexpected return of %d bytes.\n", + bytes); +#endif + return FDC_UNKNOWN; + } + if (reply_buffer[0] == 0x80) { + printf("FDC is a post-1991 82077\n"); + return FDC_82077; /* Revised 82077AA passes all the tests */ + } + switch (reply_buffer[0] >> 5) { + case 0x0: + /* Either a 82078-1 or a 82078SL running at 5Volt */ + printf("FDC is an 82078.\n"); + return FDC_82078; + case 0x1: + printf("FDC is a 44pin 82078\n"); + return FDC_82078; + case 0x2: + printf("FDC is a S82078B\n"); + return FDC_S82078B; + case 0x3: + printf("FDC is a National Semiconductor PC87306\n"); + return FDC_87306; + default: + printf("FDC init: 82078 variant with unknown PARTID=%d.\n", + reply_buffer[0] >> 5); + return FDC_82078_UNKN; + } +} /* get_fdc_version */ + + +static int floppy_init(void) +{ +#ifdef MDEBUG + printf("floppy_init\n"); +#endif + fdc_state.in_sync = 0; + fdc_state.spec1 = -1; + fdc_state.spec2 = -1; + fdc_state.dtr = -1; + fdc_state.dor = DOR_NO_RESET; + fdc_state.version = FDC_UNKNOWN; + reset_fdc(); + /* Try to determine the floppy controller type */ + fdc_state.version = get_fdc_version(); + if (fdc_state.version == FDC_NONE) { + return -1; + } + floppy_reset(); +#ifdef MDEBUG + printf("fdc_state.version = %x\n", fdc_state.version); +#endif + return 0; +} + +static void floppy_reset(void) +{ +#ifdef MDEBUG + printf("floppy_reset\n"); +#endif + floppy_motor_off(FD_DRIVE); + reset_fdc(); + fdc_dtr(DISK_H1440_RATE); + /* program data rate via ccr */ + collect_interrupt(); + fdc_configure(USE_IMPLIED_SEEK, USE_FIFO, FIFO_THRESHOLD, + TRACK_PRECOMPENSATION); + fdc_specify(DRIVE_H1440_HLT, DRIVE_H1440_HUT, DRIVE_H1440_SRT); + set_drive(FD_DRIVE); + floppy_recalibrate(); + fdc_state.in_sync = 1; +} + +static void floppy_fini(struct dev *dev __unused) +{ + /* Disable the floppy and the floppy drive controller */ + set_dor(0, 0); +} + +static int floppy_probe(struct dev *dev, unsigned short *probe_addrs) +{ + struct disk *disk = (struct disk *)dev; + unsigned short addr; + int index; + + if (!probe_addrs || !*probe_addrs) + return 0; + index = dev->index +1; + if (dev->how_probe == PROBE_AWAKE) { + index--; + } + for(; (index >= 0) && (addr = probe_addrs[index]); index++) { + /* FIXME handle multiple drives per controller */ + /* FIXME test to see if I have a drive or a disk in + * the driver during the probe routine. + */ + /* FIXME make this work under the normal bios */ + FD_BASE = addr; + if (floppy_init() != 0) { + /* nothing at this address */ + continue; + } + /* O.k. I have a floppy controller */ + disk->hw_sector_size = SECTOR_SIZE; + disk->sectors_per_read = DISK_H1440_SECT; + disk->sectors = DISK_H1440_HEAD*DISK_H1440_TRACK*DISK_H1440_SECT; + dev->index = index; + dev->disable = floppy_fini; + disk->read = floppy_read; + return 1; + } + dev->index = -1; + return 0; +} + +static unsigned short floppy_ioaddrs[] = +{ + 0x3F0, 0x370, 0 +}; +ISA_ROM("pc_floppy", "Generic PC Floppy support") + +static struct isa_driver floppy_isa_driver __isa_driver = { + .type = FLOPPY_DRIVER, + .name = "PC flopyy", + .probe = floppy_probe, + .ioaddrs = floppy_ioaddrs, +}; diff --git a/src/drivers/net/3c509.c b/src/drivers/net/3c509.c new file mode 100644 index 00000000..0a7187ed --- /dev/null +++ b/src/drivers/net/3c509.c @@ -0,0 +1,671 @@ +/************************************************************************** +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 "etherboot.h" +#include "nic.h" +#include "isa.h" +#include "timer.h" +#include "3c509.h" + +static unsigned short eth_nic_base; +static enum { none, bnc, utp } connector = none; /* for 3C509 */ + +#ifdef INCLUDE_3C529 +/* + * This table and 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) + * + */ +struct el3_mca_adapters_struct { + const char *name; + int id; +}; +static struct el3_mca_adapters_struct 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 }, + { NULL, 0 }, +}; +#endif + +/************************************************************************** +ETH_RESET - Reset adapter +***************************************************************************/ +static void t509_reset(struct nic *nic) +{ + int i; + + /*********************************************************** + Reset 3Com 509 card + *************************************************************/ + + /* stop card */ + outw(RX_DISABLE, BASE + EP_COMMAND); + outw(RX_DISCARD_TOP_PACK, BASE + EP_COMMAND); + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) + ; + outw(TX_DISABLE, BASE + EP_COMMAND); + outw(STOP_TRANSCEIVER, BASE + EP_COMMAND); + udelay(1000); + outw(RX_RESET, BASE + EP_COMMAND); + outw(TX_RESET, BASE + EP_COMMAND); + outw(C_INTR_LATCH, BASE + EP_COMMAND); + outw(SET_RD_0_MASK, BASE + EP_COMMAND); + outw(SET_INTR_MASK, BASE + EP_COMMAND); + outw(SET_RX_FILTER, BASE + EP_COMMAND); + + /* + * initialize card + */ + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) + ; + + GO_WINDOW(0); + + /* Disable the card */ + outw(0, BASE + EP_W0_CONFIG_CTRL); + + /* Configure IRQ to none */ + outw(SET_IRQ(0), BASE + EP_W0_RESOURCE_CFG); + + /* Enable the card */ + outw(ENABLE_DRQ_IRQ, BASE + EP_W0_CONFIG_CTRL); + + GO_WINDOW(2); + + /* Reload the ether_addr. */ + for (i = 0; i < ETH_ALEN; i++) + outb(nic->node_addr[i], BASE + EP_W2_ADDR_0 + i); + + outw(RX_RESET, BASE + EP_COMMAND); + outw(TX_RESET, BASE + EP_COMMAND); + + /* Window 1 is operating window */ + GO_WINDOW(1); + for (i = 0; i < 31; i++) + inb(BASE + EP_W1_TX_STATUS); + + /* get rid of stray intr's */ + outw(ACK_INTR | 0xff, BASE + EP_COMMAND); + + outw(SET_RD_0_MASK | S_5_INTS, BASE + EP_COMMAND); + + outw(SET_INTR_MASK, BASE + EP_COMMAND); + + outw(SET_RX_FILTER | FIL_GROUP | FIL_INDIVIDUAL | FIL_BRDCST, BASE + EP_COMMAND); + + /* configure BNC */ + if (connector == bnc) { + outw(START_TRANSCEIVER, BASE + EP_COMMAND); + udelay(1000); + } + /* configure UTP */ + else if (connector == utp) { + GO_WINDOW(4); + outw(ENABLE_UTP, BASE + EP_W4_MEDIA_TYPE); + sleep(2); /* Give time for media to negotiate */ + GO_WINDOW(1); + } + + /* start transceiver and receiver */ + outw(RX_ENABLE, BASE + EP_COMMAND); + outw(TX_ENABLE, BASE + EP_COMMAND); + + /* set early threshold for minimal packet length */ + outw(SET_RX_EARLY_THRESH | ETH_ZLEN, BASE + EP_COMMAND); + outw(SET_TX_START_THRESH | 16, BASE + EP_COMMAND); +} + +/************************************************************************** +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(BASE + EP_W1_TX_STATUS)) & TXS_COMPLETE ) { + if (status & (TXS_UNDERRUN|TXS_MAX_COLLISION|TXS_STATUS_OVERFLOW)) { + outw(TX_RESET, BASE + EP_COMMAND); + outw(TX_ENABLE, BASE + EP_COMMAND); + } + outb(0x0, BASE + EP_W1_TX_STATUS); + } + + while (inw(BASE + EP_W1_FREE_TX) < (unsigned short)len + pad + 4) + ; /* no room in FIFO */ + + outw(len, BASE + EP_W1_TX_PIO_WR_1); + outw(0x0, BASE + EP_W1_TX_PIO_WR_1); /* Second dword meaningless */ + + /* write packet */ + outsw(BASE + EP_W1_TX_PIO_WR_1, d, ETH_ALEN/2); + outsw(BASE + EP_W1_TX_PIO_WR_1, nic->node_addr, ETH_ALEN/2); + outw(t, BASE + EP_W1_TX_PIO_WR_1); + outsw(BASE + EP_W1_TX_PIO_WR_1, p, s / 2); + if (s & 1) + outb(*(p+s - 1), BASE + EP_W1_TX_PIO_WR_1); + + while (pad--) + outb(0, BASE + EP_W1_TX_PIO_WR_1); /* Padding */ + + /* wait for Tx complete */ + while((inw(BASE + 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(BASE + 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), BASE + EP_COMMAND); + outw(C_INTR_LATCH, BASE + EP_COMMAND); + + return 0; + } + + status = inw(BASE + EP_W1_RX_STATUS); +#ifdef EDEBUG + printf("*%hX*",status); +#endif + + if (status & ERR_RX) { + outw(RX_DISCARD_TOP_PACK, BASE + 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(BASE + EP_W1_RX_PIO_RD_1, nic->packet, rx_fifo / 2); + if(rx_fifo & 1) + nic->packet[rx_fifo-1]=inb(BASE + EP_W1_RX_PIO_RD_1); + nic->packetlen=rx_fifo; + + while(1) { + status = inw(BASE + EP_W1_RX_STATUS); +#ifdef EDEBUG + printf("*%hX*",status); +#endif + rx_fifo = status & RX_BYTES_MASK; + if(rx_fifo>0) { + insw(BASE + 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(BASE + 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, BASE + EP_COMMAND); + while (inw(BASE + 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); +} + +/************************************************************************* + 3Com 509 - specific routines +**************************************************************************/ + +static int +eeprom_rdy(void) +{ + int i; + + for (i = 0; is_eeprom_busy(IS_BASE) && 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. we must have set the window + * before + */ +static int +get_e(int offset) +{ + if (!eeprom_rdy()) + return (0xffff); + outw(EEPROM_CMD_RD | offset, IS_BASE + EP_W0_EEPROM_COMMAND); + if (!eeprom_rdy()) + return (0xffff); + return (inw(IS_BASE + EP_W0_EEPROM_DATA)); +} + +static int +send_ID_sequence(int port) +{ + int cx, al; + + for (al = 0xff, cx = 0; cx < 255; cx++) { + outb(al, port); + al <<= 1; + if (al & 0x100) + al ^= 0xcf; + } + return (1); +} + + +/* + * 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 int +get_eeprom_data(int id_port, int offset) +{ + int i, data = 0; + outb(0x80 + offset, id_port); + /* Do we really need this wait? Won't be noticeable anyway */ + udelay(10000); + for (i = 0; i < 16; i++) + data = (data << 1) | (inw(id_port) & 1); + return (data); +} + +static void __t509_disable(void) +{ + outb(0xc0, EP_ID_PORT); +} + +static void t509_disable(struct dev *dev) +{ + struct nic *nic = (struct nic *)dev; + /* reset and disable merge */ + t509_reset(nic); + __t509_disable(); +} + +static void t509_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 +***************************************************************************/ +#ifdef INCLUDE_3C529 +static int t529_probe(struct dev *dev, unsigned short *probe_addrs __unused) +#else +static int t509_probe(struct dev *dev, unsigned short *probe_addrs __unused) +#endif +{ + struct nic *nic = (struct nic *)dev; + /* common variables */ + int i; + int failcount; + +#ifdef INCLUDE_3C529 + struct el3_mca_adapters_struct *mcafound = NULL; + int mca_pos4 = 0, mca_pos5 = 0, mca_irq = 0; +#endif + + __t509_disable(); /* in case board was active */ + + for (failcount = 0; failcount < 100; failcount++) { + int data, j, io_base, id_port; + unsigned short k; + int ep_current_tag; + short *p; +#ifdef INCLUDE_3C529 + int curboard; +#endif + + id_port = EP_ID_PORT; + ep_current_tag = EP_LAST_TAG + 1; + + /********************************************************* + Search for 3Com 509 card + ***********************************************************/ +#ifdef INCLUDE_3C529 + /* + * XXX: We should really check to make sure we have an MCA + * bus controller before going ahead with this... + * + * For now, we avoid any hassle by making it a compile + * time option. + * + */ + /* printf("\nWarning: Assuming presence of MCA bus\n"); */ + + /* Make sure motherboard setup is off */ + outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG); + + /* Cycle through slots */ + for(curboard=0; curboard= EP_MAX_BOARDS) + goto no3c509; + + /* + * The iobase was found and MFG_ID was 0x6d50. PROD_ID should be + * 0x9[0-f]50 + */ + GO_WINDOW(0); + k = get_e(EEPROM_PROD_ID); +#ifdef INCLUDE_3C529 + /* + * On MCA, the PROD_ID matches the MCA card ID (POS0+POS1) + */ + if (mcafound) { + if (mcafound->id != k) { + printf("MCA: PROD_ID in EEPROM does not match MCA card ID! (%hX != %hX)\n", k, mcafound->id); + goto no3c509; + } + } else { /* for ISA/EISA */ + if ((k & 0xf0ff) != (PROD_ID & 0xf0ff)) + goto no3c509; + } +#else + if ((k & 0xf0ff) != (PROD_ID & 0xf0ff)) + goto no3c509; +#endif + +#ifdef INCLUDE_3C529 + if (mcafound) { + printf("%s board found on MCA at %#hx IRQ %d -", + mcafound->name, eth_nic_base, mca_irq); + } else { +#endif + if(eth_nic_base >= EP_EISA_START) + printf("3C5x9 board on EISA at %#hx - ",eth_nic_base); + else + printf("3C5x9 board on ISA at %#hx - ",eth_nic_base); +#ifdef INCLUDE_3C529 + } +#endif + + /* test for presence of connectors */ + i = inw(IS_BASE + EP_W0_CONFIG_CTRL); + j = (inw(IS_BASE + 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"); + goto no3c509; + } + break; + case 1: + if (i & IS_AUI) + printf("10base5\n"); + else { + printf("10base5 not present\n"); + goto no3c509; + } + break; + case 3: + if (i & IS_BNC) { + printf("10base2\n"); + connector = bnc; + } + else { + printf("10base2 not present\n"); + goto no3c509; + } + break; + default: + printf("unknown connector\n"); + goto no3c509; + } + /* + * Read the station address from the eeprom + */ + p = (unsigned short *) nic->node_addr; + for (i = 0; i < ETH_ALEN / 2; i++) { + GO_WINDOW(0); + p[i] = htons(get_e(i)); + GO_WINDOW(2); + outw(ntohs(p[i]), BASE + EP_W2_ADDR_0 + (i * 2)); + } + printf("Ethernet address: %!\n", nic->node_addr); + t509_reset(nic); + + nic->irqno = 0; + nic->ioaddr = eth_nic_base; + + dev->disable = t509_disable; + nic->poll = t509_poll; + nic->transmit = t509_transmit; + nic->irq = t509_irq; + + /* Based on PnP ISA map */ + dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR); + dev->devid.device_id = htons(0x80f7); + return 1; +no3c509: + continue; + /* printf("(probe fail)"); */ + } + return 0; +} + +#ifdef INCLUDE_3C509 +static struct isa_driver t509_driver __isa_driver = { + .type = NIC_DRIVER, + .name = "3C509", + .probe = t509_probe, + .ioaddrs = 0, +}; +#endif + +#ifdef INCLUDE_3C529 +static struct isa_driver t529_driver __isa_driver = { + .type = NIC_DRIVER, + .name = "3C529", + .probe = t529_probe, + .ioaddrs = 0, +}; +#endif + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/src/drivers/net/3c509.h b/src/drivers/net/3c509.h new file mode 100644 index 00000000..243ba898 --- /dev/null +++ b/src/drivers/net/3c509.h @@ -0,0 +1,399 @@ +/* + * 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 + + */ + +/* + * 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_LAST_TAG 0xd7 +#define EP_MAX_BOARDS 16 +#ifndef EP_ID_PORT +#define EP_ID_PORT 0x100 +#endif + +/* + * some macros to acces long named fields + */ +#define IS_BASE (eth_nic_base) +#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) +#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(x) outw(WINDOW_SELECT|(x), BASE+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. + * + **************************************************************************/ + +#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 ACTIVATE_ADAPTER_TO_CONFIG 0xff /* to the id_port */ +#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) + + /* EISA support */ +#define EP_EISA_START 0x1000 +#define EP_EISA_W0 0x0c80 + +#ifdef INCLUDE_3C529 + /* MCA support */ +#define MCA_MOTHERBOARD_SETUP_REG 0x94 +#define MCA_ADAPTER_SETUP_REG 0x96 +#define MCA_MAX_SLOT_NR 8 +#define MCA_POS_REG(n) (0x100+(n)) +#endif + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/src/drivers/net/3c515.c b/src/drivers/net/3c515.c new file mode 100644 index 00000000..f185acef --- /dev/null +++ b/src/drivers/net/3c515.c @@ -0,0 +1,814 @@ +/* +* 3c515.c -- 3COM 3C515 Fast Etherlink ISA 10/100BASE-TX driver for etherboot +* Copyright (C) 2002 Timothy Legge +* +* 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 ISA Plug & Play support Linux Kernel +* Copyright (C) 2000 Shusuke Nisiyama 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 +* *********************************************************/ + + +#define ISA_PNP +/*#define EDEBUG1*/ + +/* to get some global routines like printf */ +#include "etherboot.h" +/* to get the interface to the body of the program */ +#include "nic.h" +#include "isa.h" +#include "timer.h" + +#ifdef ISA_PNP + +static void t3c515_wait(unsigned int nticks) +{ + unsigned int to = currticks() + nticks; + while (currticks() < to) + /* wait */ ; +} +#endif +/* TJL definations */ +#define HZ 100 +#define u16 unsigned short +#define u32 unsigned long +#define s16 signed short +#define s32 signed long +static unsigned short eth_nic_base; +#define BASE (eth_nic_base) +static int if_port; +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; +*/ + +#ifdef DRIVER_DEBUG +static int corkscrew_debug = DRIVER_DEBUG; +#else +static int corkscrew_debug = 1; +#endif + +#define CORKSCREW_ID 10 + +#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), 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[8] = { -1, -1, -1, -1, -1, -1, -1, -1, }; + +/* End Brought directly from 3c515.c by Becker */ + +/************************************************************************** +RESET - Reset adapter +***************************************************************************/ +static void t515_reset(struct nic *nic) +{ + int ioaddr = BASE; + union wn3_config config; + int i; + + /* Before initializing select the active media port. */ + EL3WINDOW(3); + if (vp->full_duplex) + outb(0x20, ioaddr + Wn3_MAC_Ctrl); /* Set the full-duplex bit. */ + config.i = inl(ioaddr + Wn3_Config); + + if (vp->media_override != 7) { + if (corkscrew_debug > 1) + printf("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; + + if (corkscrew_debug > 1) + printf("Initial media type %s.\n", + media_tbl[if_port].name); + } else + if_port = vp->default_media; + + config.u.xcvr = if_port; + outl(config.i, ioaddr + Wn3_Config); + + if (corkscrew_debug > 1) { + printf("corkscrew_open() InternalConfig 0x%hX.\n", + config.i); + } + + outw(TxReset, ioaddr + EL3_CMD); + for (i = 20; i >= 0; i--) + if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress)) + break; + + outw(RxReset, ioaddr + EL3_CMD); + /* Wait a few ticks for the RxReset command to complete. */ + for (i = 20; i >= 0; i--) + if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress)) + break; + + outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD); + + if (corkscrew_debug > 1) { + EL3WINDOW(4); + printf("FIXME: fix print for irq, not 9"); + printf("corkscrew_open() irq %d media status 0x%hX.\n", + 9, inw(ioaddr + Wn4_Media)); + } + + /* 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], ioaddr + i); + for (; i < 12; i += 2) + outw(0, ioaddr + i); + + if (if_port == 3) + /* Start the thinnet transceiver. We should really wait 50ms... */ + outw(StartCoax, ioaddr + EL3_CMD); + EL3WINDOW(4); + outw((inw(ioaddr + Wn4_Media) & ~(Media_10TP | Media_SQE)) | + media_tbl[if_port].media_bits, ioaddr + Wn4_Media); + + /* Switch to the stats window, and clear all stats by reading. */ +/* outw(StatsDisable, ioaddr + EL3_CMD);*/ + EL3WINDOW(6); + for (i = 0; i < 10; i++) + inb(ioaddr + i); + inw(ioaddr + 10); + inw(ioaddr + 12); + /* New: On the Vortex we must also clear the BadSSD counter. */ + EL3WINDOW(4); + inb(ioaddr + 12); + /* ..and on the Boomerang we enable the extra statistics bits. */ + outw(0x0040, 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; + if (corkscrew_debug > 2) + printf(" 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, 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, ioaddr + DownListPtr); + } + /* Set receiver mode: presumably accept b-case and phys addr only. */ + outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm, + ioaddr + EL3_CMD); + + outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */ + outw(TxEnable, 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), ioaddr + EL3_CMD); + /* Ack all pending events, and set active indicator mask. */ + outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, + ioaddr + EL3_CMD); + outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull + | (vp->bus_master ? DMADone : 0) | UpComplete | DownComplete, + 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(BASE + EL3_STATUS); + + if ((cst & RxComplete) == 0) { + /* Ack all pending events, and set active indicator mask. */ + outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, + BASE + EL3_CMD); + outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | + StatsFull | (vp-> + bus_master ? DMADone : 0) | UpComplete | + DownComplete, BASE + EL3_CMD); + return 0; + } + status = inw(BASE + RxStatus); + + if (status & RxDError) { + printf("RxDError\n"); + outw(RxDiscard, BASE + EL3_CMD); + return 0; + } + + rx_fifo = status & RX_BYTES_MASK; + if (rx_fifo == 0) + return 0; + + if ( ! retrieve ) return 1; + +#ifdef EDEBUG + printf("[l=%d", rx_fifo); +#endif + insw(BASE + RX_FIFO, nic->packet, rx_fifo / 2); + if (rx_fifo & 1) + nic->packet[rx_fifo - 1] = inb(BASE + RX_FIFO); + nic->packetlen = rx_fifo; + + while (1) { + status = inw(BASE + RxStatus); +#ifdef EDEBUG + printf("0x%hX*", status); +#endif + rx_fifo = status & RX_BYTES_MASK; + + if (rx_fifo > 0) { + insw(BASE + RX_FIFO, nic->packet + nic->packetlen, + rx_fifo / 2); + if (rx_fifo & 1) + nic->packet[nic->packetlen + rx_fifo - 1] = + inb(BASE + RX_FIFO); + nic->packetlen += rx_fifo; +#ifdef EDEBUG + printf("+%d", rx_fifo); +#endif + } + if ((status & RxComplete) == 0) { +#ifdef EDEBUG + printf("=%d", nic->packetlen); +#endif + break; + } + udelay(1000); + } + + /* acknowledge reception of packet */ + outw(RxDiscard, BASE + EL3_CMD); + while (inw(BASE + EL3_STATUS) & CmdInProgress); +#ifdef EDEBUG + { + 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) + printf(",t=0x%hX,b]", type); + else + printf(",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; + +#ifdef EDEBUG + printf("{l=%d,t=0x%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 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(BASE + TxStatus)) & TxComplete) { + /*if(status & (TXS_UNDERRUN|0x88|TXS_STATUS_OVERFLOW)) { */ + outw(TxReset, BASE + EL3_CMD); + outw(TxEnable, BASE + EL3_CMD); +/* } */ + + outb(0x0, BASE + TxStatus); + } + + while (inw(BASE + TxFree) < len + pad + 4) { + /* no room in FIFO */ + } + + outw(len, BASE + TX_FIFO); + outw(0x0, BASE + TX_FIFO); /* Second dword meaningless */ + + /* write packet */ + outsw(BASE + TX_FIFO, d, ETH_ALEN / 2); + outsw(BASE + TX_FIFO, nic->node_addr, ETH_ALEN / 2); + outw(t, BASE + TX_FIFO); + outsw(BASE + TX_FIFO, p, s / 2); + + if (s & 1) + outb(*(p + s - 1), BASE + TX_FIFO); + + while (pad--) + outb(0, BASE + TX_FIFO); /* Padding */ + + /* wait for Tx complete */ + while ((inw(BASE + EL3_STATUS) & CmdInProgress) != 0); +} + +/************************************************************************** +DISABLE - Turn off ethernet interface +***************************************************************************/ +static void t515_disable(struct dev *dev) +{ + struct nic *nic = (struct nic *) dev; + + /* merge reset an disable */ + 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, BASE + EL3_CMD); + outw(TxDisable, BASE + EL3_CMD); + + if (if_port == XCVR_10base2) + /* Turn off thinnet power. Green! */ + outw(StopCoax, BASE + EL3_CMD); + + + outw(SetIntrEnb | 0x0000, BASE + EL3_CMD); +#ifdef ISA_PNP + /*Deactivate */ +/* ACTIVATE; + WRITE_DATA(0); + */ +#endif + 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; + } +} + +/************************************************************************** +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 +***************************************************************************/ +void config_pnp_device(void); + +static int t515_probe(struct dev *dev, + unsigned short *probe_addrs __unused) +{ + struct nic *nic = (struct nic *) dev; + /* Direct copy from Beckers 3c515.c removing any ISAPNP sections */ + int cards_found = 0; + static int ioaddr; +#ifdef ISA_PNP + config_pnp_device(); +#endif + /* Check all locations on the ISA bus -- evil! */ + for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x20) { + int irq; + + /* Check the resource configuration for a matching ioaddr. */ + if ((inw(ioaddr + 0x2002) & 0x1f0) != (ioaddr & 0x1f0)) + continue; + /* Verify by reading the device ID from the EEPROM. */ + { + int timer; + outw(EEPROM_Read + 7, 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; + } + if (inw(ioaddr + Wn0EepromData) != 0x6d50) + continue; + } + printf + ("3c515 Resource configuration register 0x%hX, DCR 0x%hX.\n", + inl(ioaddr + 0x2002), inw(ioaddr + 0x2000)); + irq = inw(ioaddr + 0x2002) & 15; + BASE = ioaddr; + corkscrew_found_device(BASE, irq, CORKSCREW_ID, + options[cards_found], nic); + cards_found++; + } + if (corkscrew_debug) + printf("%d 3c515 cards found.\n", cards_found); + + if (cards_found > 0) { + t515_reset(nic); + + nic->irqno = 0; + nic->ioaddr = BASE; + + dev->disable = t515_disable; + nic->poll = t515_poll; + nic->transmit = t515_transmit; + nic->irq = t515_irq; + + /* Based on PnP ISA map */ + dev->devid.vendor_id = htons(ISAPNP_VENDOR('T', 'C', 'M')); + dev->devid.device_id = htons(0x5051); + return 1; + } else + return 0; + +} + +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; + ioaddr = BASE; + + 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); +#ifdef EDEBUG1 + printf("Value %d: %hX ", i, eeprom[i]); +#endif + 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); + + printf("%!", nic->node_addr); + if (eeprom[16] == 0x11c7) { /* Corkscrew */ + + } + printf(", IRQ %d\n", irq); + /* Tell them about an invalid IRQ. */ + if (corkscrew_debug && (irq <= 0 || irq > 15)) + printf + (" *** 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); + if (corkscrew_debug > 1) + printf + (" 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 isa_driver t515_driver __isa_driver = { + .type = NIC_DRIVER, + .name = "3C515", + .probe = t515_probe, + .ioaddrs = 0, +}; diff --git a/src/drivers/net/3c515.txt b/src/drivers/net/3c515.txt new file mode 100644 index 00000000..8f7b3a72 --- /dev/null +++ b/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 + +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/src/drivers/net/3c595.c b/src/drivers/net/3c595.c new file mode 100644 index 00000000..e8fc377d --- /dev/null +++ b/src/drivers/net/3c595.c @@ -0,0 +1,550 @@ +/* +* 3c595.c -- 3COM 3C595 Fast Etherlink III PCI driver for etherboot +* +* Copyright (C) 2000 Shusuke Nisiyama +* 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 +* +* timlegge 08-24-2003 Add Multicast Support +*/ + +/* #define EDEBUG */ + +#include "etherboot.h" +#include "nic.h" +#include "pci.h" +#include "3c595.h" +#include "timer.h" + +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 < 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 <ioaddr == 0) + return 0; +/* eth_nic_base = probeaddrs[0] & ~3; */ + eth_nic_base = pci->ioaddr; + + nic->irqno = 0; + nic->ioaddr = pci->ioaddr & ~3; + + 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)); + } + + printf("Ethernet address: %!\n", nic->node_addr); + + t595_reset(nic); + dev->disable = t595_disable; + nic->poll = t595_poll; + nic->transmit = t595_transmit; + nic->irq = t595_irq; + return 1; + +} + +static struct pci_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"), +}; + +static struct pci_driver t595_driver __pci_driver = { + .type = NIC_DRIVER, + .name = "3C595", + .probe = t595_probe, + .ids = t595_nics, + .id_count = sizeof(t595_nics)/sizeof(t595_nics[0]), + .class = 0, +}; + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ + diff --git a/src/drivers/net/3c595.h b/src/drivers/net/3c595.h new file mode 100644 index 00000000..49d8d9b0 --- /dev/null +++ b/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 + * 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/src/drivers/net/3c90x.c b/src/drivers/net/3c90x.c new file mode 100644 index 00000000..1c8b7e44 --- /dev/null +++ b/src/drivers/net/3c90x.c @@ -0,0 +1,996 @@ +/* + * 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. + * + */ + +#include "etherboot.h" +#include "nic.h" +#include "pci.h" +#include "timer.h" + +#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 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. **/ + 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; + + 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 **/ + load_timer2(10*TICKS_PER_MS); /* Give it 10 ms */ + while (!(inw(INF_3C90X.IOAddr + regCommandIntStatus_w)&0x0004) && + timer2_running()) + ; + + 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 i, 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)) **/ + for(i=0;i<40000;i++); + while((INF_3C90X.ReceiveUPD.UpPktStatus & ((1<<14) | (1<<15))) == 0) + for(i=0;i<40000;i++); + + /** 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 dev *dev __unused) +{ + /* reset and disable merge */ + 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 dev *dev, struct pci_device *pci) +{ + struct nic *nic = (struct nic *)dev; + 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 & ~3; + nic->irqno = 0; + + 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.00 " + "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; + printf("MAC Address = %!\n", INF_3C90X.HWAddr); + + /* 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;inode_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 **/ + dev->disable = a3c90x_disable; + nic->poll = a3c90x_poll; + nic->transmit = a3c90x_transmit; + nic->irq = a3c90x_irq; + + return 1; +} + + +static struct pci_id a3c90x_nics[] = { +/* Original 90x revisions: */ +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"), +}; + +static struct pci_driver a3c90x_driver __pci_driver = { + .type = NIC_DRIVER, + .name = "3C90X", + .probe = a3c90x_probe, + .ids = a3c90x_nics, + .id_count = sizeof(a3c90x_nics)/sizeof(a3c90x_nics[0]), + .class = 0, +}; diff --git a/src/drivers/net/3c90x.txt b/src/drivers/net/3c90x.txt new file mode 100644 index 00000000..3d6746c5 --- /dev/null +++ b/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 .... + 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 , 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/src/drivers/net/cs89x0.c b/src/drivers/net/cs89x0.c new file mode 100644 index 00000000..42de1f2a --- /dev/null +++ b/src/drivers/net/cs89x0.c @@ -0,0 +1,720 @@ +#ifdef ALLMULTI +#error multicast support is not yet implemented +#endif +/* 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 + + * 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 + + * 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 + + * 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 + + * Synched with Russel Nelson's current code (v1.00) + + * 2 + + Thu Nov 12 18:00:00 1996 Markus Gutschke + + * Cleaned up some of the code and tried to optimize the code size. + + * 1.5 + + Sun Nov 10 16:30:00 1996 Markus Gutschke + + * 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 "etherboot.h" +#include "nic.h" +#include "isa.h" +#include "cs89x0.h" + +#ifndef EMBEDDED +static unsigned short eth_nic_base; +#else +static unsigned long eth_nic_base; +#endif +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_disable(struct dev *dev) +{ + struct nic *nic = (struct nic *)dev; + cs89x0_reset(nic); +} + +static void cs89x0_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 cs89x0_probe(struct dev *dev, unsigned short *probe_addrs __unused) +{ + struct nic *nic = (struct nic *)dev; + static const unsigned int netcard_portlist[] = { +#ifdef CS_SCAN + CS_SCAN, +#else /* use "conservative" default values for autoprobing */ +#ifndef EMBEDDED + 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 +#endif + 0}; + + int i, result = -1; + unsigned rev_type = 0, ioaddr, ioidx, isa_cnf, cs_revision; + unsigned short eeprom_buff[CHKSUM_LEN]; + + + for (ioidx = 0; (ioaddr=netcard_portlist[ioidx++]) != 0; ) { + /* 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) + continue; + outw(PP_ChipID, ioaddr + ADD_PORT); + } + + if (inw(ioaddr + DATA_PORT) != CHIP_EISA_ID_SIG) + continue; + eth_nic_base = 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); + continue; } + 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); + continue; } + 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); + continue; } + + /* 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; } + + /* Retrieve and print the ethernet address. */ + for (i=0; inode_addr[i] = ((unsigned char *)eeprom_buff)[i]; + } + printf("%!\n", 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); + } + printf("\n%!\n", 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); + continue; + } +#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); + + break; +#ifdef EMBEDDED + error: + writereg(PP_LineCTL, readreg(PP_LineCTL) & + ~(SERIAL_TX_ON | SERIAL_RX_ON)); + outw(PP_ChipID, eth_nic_base + ADD_PORT); + continue; +#endif + } + + if (ioaddr == 0) + return (0); + + nic->irqno = 0; + nic->ioaddr = ioaddr; + + dev->disable = cs89x0_disable; + nic->poll = cs89x0_poll; + nic->transmit = cs89x0_transmit; + nic->irq = cs89x0_irq; + + /* Based on PnP ISA map */ + dev->devid.vendor_id = htons(ISAPNP_VENDOR('C','S','C')); + dev->devid.device_id = htons(0x0007); + return 1; +} + +static struct isa_driver cs89x0_driver __isa_driver = { + .type = NIC_DRIVER, + .name = "CS89x0", + .probe = cs89x0_probe, + .ioaddrs = 0, +}; + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ + diff --git a/src/drivers/net/cs89x0.h b/src/drivers/net/cs89x0.h new file mode 100644 index 00000000..b9b36251 --- /dev/null +++ b/src/drivers/net/cs89x0.h @@ -0,0 +1,461 @@ +/* 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/src/drivers/net/cs89x0.txt b/src/drivers/net/cs89x0.txt new file mode 100644 index 00000000..4927641d --- /dev/null +++ b/src/drivers/net/cs89x0.txt @@ -0,0 +1,26 @@ +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 + for providing an evaluation NIC and for sponsoring the + development of this driver. + + Randall Sears + Deva Bodas + Andreas Kraemer + 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 + 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/src/drivers/net/davicom.c b/src/drivers/net/davicom.c new file mode 100644 index 00000000..1d0235f4 --- /dev/null +++ b/src/drivers/net/davicom.c @@ -0,0 +1,720 @@ +#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 "pci.h" + +#undef DAVICOM_DEBUG +#undef DAVICOM_DEBUG_WHERE + +#define TX_TIME_OUT 2*TICKS_PER_SEC + +typedef unsigned char u8; +typedef signed char s8; +typedef unsigned short u16; +typedef signed short s16; +typedef unsigned int u32; +typedef signed int s32; + +/* 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 */ +/*********************************************************************/ + +/* 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 +static struct txdesc txd[NTXD] __attribute__ ((aligned(4))); +static unsigned char txb[BUFLEN] __attribute__ ((aligned(4))); + +/* receive descriptor(s) and buffer(s) */ +#define NRXD 4 +static struct rxdesc rxd[NRXD] __attribute__ ((aligned(4))); +static unsigned char rxb[NRXD * BUFLEN] __attribute__ ((aligned(4))); +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 dev *dev, 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 dev *dev); +#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; inode_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= 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 dev *dev) +{ + struct nic *nic = (struct nic *)dev; + 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 dev *dev, struct pci_device *pci) +{ + struct nic *nic = (struct nic *)dev; + unsigned int i; + + whereami("davicom_probe\n"); + + if (pci->ioaddr == 0) + return 0; + + vendor = pci->vendor; + dev_id = pci->dev_id; + ioaddr = pci->ioaddr & ~3; + + nic->irqno = 0; + nic->ioaddr = pci->ioaddr & ~3; + + /* wakeup chip */ + pcibios_write_config_dword(pci->bus, pci->devfn, 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; inode_addr[i] = ee_data[20+i]; + + printf("Davicom %! at ioaddr %#hX\n", nic->node_addr, ioaddr); + + /* initialize device */ + davicom_reset(nic); + + dev->disable = davicom_disable; + nic->poll = davicom_poll; + nic->transmit = davicom_transmit; + nic->irq = davicom_irq; + + return 1; +} + +static struct pci_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 */ +}; + +static struct pci_driver davicom_driver __pci_driver = { + .type = NIC_DRIVER, + .name = "DAVICOM", + .probe = davicom_probe, + .ids = davicom_nics, + .id_count = sizeof(davicom_nics)/sizeof(davicom_nics[0]), + .class = 0, +}; diff --git a/src/drivers/net/depca.c b/src/drivers/net/depca.c new file mode 100644 index 00000000..29266b10 --- /dev/null +++ b/src/drivers/net/depca.c @@ -0,0 +1,794 @@ +/* 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 . + 0.383 22-feb-95 Fix for conflict with VESA SCSI reported by + + 0.384 17-mar-95 Fix a ring full bug reported by + 0.385 3-apr-95 Fix a recognition bug reported by + + 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 + 0.41 26-Jun-95 Added verify_area() calls in depca_ioctl() from + suggestion by + 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 + . + Add new multicasting code. + 0.421 22-Apr-96 Fix alloc_device() bug + 0.422 29-Apr-96 Fix depca_hw_init() bug + 0.423 7-Jun-96 Fix module load bug + 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 + 0.45 3-Nov-98 Added support for MCA EtherWORKS (DE210/DE212) cards + by + 0.451 5-Nov-98 Fixed mca stuff cuz I'm a dummy. + 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 + + ========================================================================= +*/ + +#include "etherboot.h" +#include "nic.h" +#include "isa.h" + +/* +** I/O addresses. Note that the 2k buffer option is not supported in +** this driver. +*/ +#define DEPCA_NICSR ioaddr+0x00 /* Network interface CSR */ +#define DEPCA_RBI ioaddr+0x02 /* RAM buffer index (2k buffer mode) */ +#define DEPCA_DATA ioaddr+0x04 /* LANCE registers' data port */ +#define DEPCA_ADDR ioaddr+0x06 /* LANCE registers' address port */ +#define DEPCA_HBASE ioaddr+0x08 /* EISA high memory base address reg. */ +#define DEPCA_PROM ioaddr+0x0c /* Ethernet address ROM data port */ +#define DEPCA_CNFG ioaddr+0x0c /* EISA Configuration port */ +#define DEPCA_RBSA ioaddr+0x0e /* RAM buffer starting address (2k buff.) */ + +/* +** These are LANCE registers addressable through 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 +*/ +#define DEPCA_IO_PORTS {0x300, 0x200, 0} + +#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... */ + +typedef long s32; +typedef unsigned long u32; +typedef short s16; +typedef unsigned short u16; +typedef char s8; +typedef unsigned char u8; + +/* +** 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 unsigned short ioaddr = 0; +static struct depca_private lp; + +/* +** Miscellaneous defines... +*/ +#define STOP_DEPCA \ + outw(CSR0, DEPCA_ADDR);\ + outw(STOP, 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 void LoadCSRs(void) +{ + outw(CSR1, DEPCA_ADDR); /* initialisation block address LSW */ + outw((u16) (lp.sh_mem & LA_MASK), DEPCA_DATA); + outw(CSR2, DEPCA_ADDR); /* initialisation block address MSW */ + outw((u16) ((lp.sh_mem & LA_MASK) >> 16), DEPCA_DATA); + outw(CSR3, DEPCA_ADDR); /* ALE control */ + outw(ACON, DEPCA_DATA); + outw(CSR0, DEPCA_ADDR); /* Point back to CSR0 */ +} + +static int InitRestartDepca(void) +{ + 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, DEPCA_ADDR); /* point back to CSR0 */ + outw(INIT, DEPCA_DATA); /* initialise DEPCA */ + + for (i = 0; i < 100 && !(inw(DEPCA_DATA) & IDON); i++) + ; + if (i < 100) { + /* clear IDON by writing a 1, and start LANCE */ + outw(IDON | STRT, 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; + nicsr = inb(DEPCA_NICSR); + nicsr = ((nicsr & ~SHE & ~RBE & ~IEN) | IM); + outb(nicsr, DEPCA_NICSR); + if (inw(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(); + InitRestartDepca(); +} + +/************************************************************************** +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 dev *dev) +{ + struct nic *nic = (struct nic *)dev; + /* reset and disable merge */ + depca_reset(nic); + + STOP_DEPCA; +} + +/************************************************************************** +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. +*/ +static int depca_probe1(struct nic *nic) +{ + u8 data, nicsr; + /* 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; + long sum, chksum; + + data = inb(DEPCA_PROM); /* clear counter on DEPCA */ + data = inb(DEPCA_PROM); /* read data */ + if (data == 0x8) { + nicsr = inb(DEPCA_NICSR); + nicsr |= AAC; + outb(nicsr, DEPCA_NICSR); + } + for (i = 0, j = 0; j < (int)sizeof(sig) && i < PROBE_LENGTH+((int)sizeof(sig))-1; ++i) { + data = inb(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; + nicsr = ((inb(DEPCA_NICSR) & ~SHE & ~RBE & ~IEN) | IM); + outb(nicsr, DEPCA_NICSR); + if (inw(DEPCA_DATA) != STOP) + return (0); + memcpy((char *)mem_start, sig, sizeof(sig)); + if (memcmp((char *)mem_start, sig, sizeof(sig)) != 0) + return (0); + for (i = 0, j = 0, sum = 0; j < 3; j++) { + sum <<= 1; + if (sum > 0xFFFF) + sum -= 0xFFFF; + sum += (u8)(nic->node_addr[i++] = inb(DEPCA_PROM)); + sum += (u16)((nic->node_addr[i++] = inb(DEPCA_PROM)) << 8); + if (sum > 0xFFFF) + sum -= 0xFFFF; + } + if (sum == 0xFFFF) + sum = 0; + chksum = (u8)inb(DEPCA_PROM); + chksum |= (u16)(inb(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, DEPCA_NICSR); + printf("%s base %#hX, memory [%#hX-%#hX], addr %!", + adapter_name[adapter], ioaddr, mem_start, mem_start + mem_len, + nic->node_addr); + if (sum != chksum) + printf(" (bad checksum)"); + putchar('\n'); + return (1); +} + +/************************************************************************** +PROBE - Look for an adapter, this routine's visible to the outside +***************************************************************************/ +static int depca_probe(struct dev *dev, unsigned short *probe_addrs) +{ + struct nic *nic = (struct nic *)dev; + static unsigned short base[] = DEPCA_IO_PORTS; + int i; + + if (probe_addrs == 0 || probe_addrs[0] == 0) + probe_addrs = base; /* Use defaults */ + for (i = 0; (ioaddr = base[i]) != 0; ++i) { + if (depca_probe1(nic)) + break; + } + if (ioaddr == 0) + return (0); + + nic->irqno = 0; + nic->ioaddr = ioaddr & ~3; + + depca_reset(nic); + /* point to NIC specific routines */ + dev->disable = depca_disable; + nic->poll = depca_poll; + nic->transmit = depca_transmit; + nic->irq = depca_irq; + + /* Based on PnP ISA map */ + dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR); + dev->devid.device_id = htons(0x80f7); + return 1; +} + +static struct isa_driver depca_driver __isa_driver = { + .type = NIC_DRIVER, + .name = "DEPCA", + .probe = depca_probe, + .ioaddrs = 0, +}; diff --git a/src/drivers/net/dmfe.c b/src/drivers/net/dmfe.c new file mode 100644 index 00000000..10c47773 --- /dev/null +++ b/src/drivers/net/dmfe.c @@ -0,0 +1,1230 @@ +/************************************************************************** +* +* dmfe.c -- Etherboot device driver for the Davicom +* DM9102/DM9102A/DM9102A+DM9801/DM9102A+DM9802 NIC fast ethernet card +* +* Written 2003-2003 by Timothy Legge +* +* 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 "pci.h" +#include "timer.h" + +/* #define EDEBUG 1 */ +#ifdef EDEBUG +#define dprintf(x) printf x +#else +#define dprintf(x) +#endif + +typedef unsigned char u8; +typedef signed char s8; +typedef unsigned short u16; +typedef signed short s16; +typedef unsigned int u32; +typedef signed int s32; + +/* 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))); + +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 int dmfe_debug; +static unsigned char dmfe_media_mode = DMFE_AUTO; +static u32 dmfe_cr6_user_set; + +/* For module input parameter */ +static int debug; +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 +***********************************************/ +/* Define the TX Descriptor */ +static struct tx_desc txd[TX_DESC_CNT] + __attribute__ ((aligned(32))); + +/* Create a static buffer of size PKT_BUF_SZ for each TX Descriptor. + All descriptors point to a part of this buffer */ +static unsigned char txb[TX_BUF_ALLOC * TX_DESC_CNT] + __attribute__ ((aligned(32))); + +/* Define the RX Descriptor */ +static struct rx_desc rxd[RX_DESC_CNT] +__attribute__ ((aligned(32))); + +/* Create a static buffer of size PKT_BUF_SZ for each RX Descriptor. + All descriptors point to a part of this buffer */ +static unsigned char rxb[RX_ALLOC_SIZE * RX_DESC_CNT] +__attribute__ ((aligned(32))); + +/* NIC specific static variables go here */ +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 u8 dmfe_sense_speed(struct nic *nic); +static void dmfe_process_mode(struct nic *nic); +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 dev *dev __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 dev *dev, struct pci_device *pci) +{ + struct nic *nic = (struct nic *) dev; + 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->name, pci->vendor, pci->dev_id); + + /* 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->dev_id << 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 */ + printf("%s: %! at ioaddr %hX\n", pci->name, 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 */ + dev->disable = dmfe_disable; + nic->poll = dmfe_poll; + nic->transmit = dmfe_transmit; + nic->irq = dmfe_irq; + + 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 = cpu_to_le32(&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 = cpu_to_le32(&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 + */ + +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; +} + + +/* + * 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 + */ + +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); + } + } +} + + +/* + * 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) +{ + 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 pci_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 */ +}; + +static struct pci_driver dmfe_driver __pci_driver = { + .type = NIC_DRIVER, + .name = "DMFE/PCI", + .probe = dmfe_probe, + .ids = dmfe_nics, + .id_count = sizeof(dmfe_nics) / sizeof(dmfe_nics[0]), + .class = 0, +}; diff --git a/src/drivers/net/e1000.c b/src/drivers/net/e1000.c new file mode 100644 index 00000000..7c572656 --- /dev/null +++ b/src/drivers/net/e1000.c @@ -0,0 +1,3713 @@ +/************************************************************************** +Etherboot - BOOTP/TFTP Bootstrap Program +Inter Pro 1000 for Etherboot +Drivers are port from Intel's Linux driver e1000-4.3.15 + +***************************************************************************/ +/******************************************************************************* + + + Copyright(c) 1999 - 2003 Intel Corporation. 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. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Contact Information: + Linux NICS + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ +/* + * Copyright (C) Archway Digital Solutions. + * + * written by Chrsitopher Li or + * 2/9/2002 + * + * Copyright (C) Linux Networx. + * Massive upgrade to work with the new intel gigabit NICs. + * + * + * Support for 82541ei & 82547ei chips from Intel's Linux driver 5.1.13 added by + * Georg Baum , sponsored by PetaMem GmbH and linkLINE Communications, Inc. + * + * 01/2004: Updated to Linux driver 5.2.22 by Georg Baum + */ + +/* 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 "pci.h" +#include "timer.h" + +typedef unsigned char *dma_addr_t; + +typedef enum { + FALSE = 0, + TRUE = 1 +} boolean_t; + +#define DEBUG 0 + + +/* Some pieces of code are disabled with #if 0 ... #endif. + * They are not deleted to show where the etherboot driver differs + * from the linux driver below the function level. + * Some member variables of the hw struct have been eliminated + * and the corresponding inplace checks inserted instead. + * Pieces such as LED handling that we definitely don't need are deleted. + * + * The following defines should not be needed normally, + * but may be helpful for debugging purposes. */ + +/* Define this if you want to program the transmission control register + * the way the Linux driver does it. */ +#undef LINUX_DRIVER_TCTL + +/* Define this to behave more like the Linux driver. */ +#undef LINUX_DRIVER + +#include "e1000_hw.h" + +/* NIC specific static variables go here */ +static struct e1000_hw hw; +static char tx_pool[128 + 16]; +static char rx_pool[128 + 16]; +static char packet[2096]; + +static struct e1000_tx_desc *tx_base; +static struct e1000_rx_desc *rx_base; + +static int tx_tail; +static int rx_tail, rx_last; + +/* Function forward declarations */ +static int e1000_setup_link(struct e1000_hw *hw); +static int e1000_setup_fiber_serdes_link(struct e1000_hw *hw); +static int e1000_setup_copper_link(struct e1000_hw *hw); +static int e1000_phy_setup_autoneg(struct e1000_hw *hw); +static void e1000_config_collision_dist(struct e1000_hw *hw); +static int e1000_config_mac_to_phy(struct e1000_hw *hw); +static int e1000_config_fc_after_link_up(struct e1000_hw *hw); +static int e1000_check_for_link(struct e1000_hw *hw); +static int e1000_wait_autoneg(struct e1000_hw *hw); +static void e1000_get_speed_and_duplex(struct e1000_hw *hw, uint16_t *speed, uint16_t *duplex); +static int e1000_read_phy_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t *phy_data); +static int e1000_read_phy_reg_ex(struct e1000_hw *hw, uint32_t reg_addr, uint16_t *phy_data); +static int e1000_write_phy_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t phy_data); +static int e1000_write_phy_reg_ex(struct e1000_hw *hw, uint32_t reg_addr, uint16_t phy_data); +static void e1000_phy_hw_reset(struct e1000_hw *hw); +static int e1000_phy_reset(struct e1000_hw *hw); +static int e1000_detect_gig_phy(struct e1000_hw *hw); +static void e1000_irq(struct nic *nic, irq_action_t action); + +/* Printing macros... */ + +#define E1000_ERR(args...) printf("e1000: " args) + +#if DEBUG >= 3 +#define E1000_DBG(args...) printf("e1000: " args) +#else +#define E1000_DBG(args...) +#endif + +#define MSGOUT(S, A, B) printk(S "\n", A, B) +#if DEBUG >= 2 +#define DEBUGFUNC(F) DEBUGOUT(F "\n"); +#else +#define DEBUGFUNC(F) +#endif +#if DEBUG >= 1 +#define DEBUGOUT(S) printf(S) +#define DEBUGOUT1(S,A) printf(S,A) +#define DEBUGOUT2(S,A,B) printf(S,A,B) +#define DEBUGOUT3(S,A,B,C) printf(S,A,B,C) +#define DEBUGOUT7(S,A,B,C,D,E,F,G) printf(S,A,B,C,D,E,F,G) +#else +#define DEBUGOUT(S) +#define DEBUGOUT1(S,A) +#define DEBUGOUT2(S,A,B) +#define DEBUGOUT3(S,A,B,C) +#define DEBUGOUT7(S,A,B,C,D,E,F,G) +#endif + +#define E1000_WRITE_REG(a, reg, value) ( \ + ((a)->mac_type >= e1000_82543) ? \ + (writel((value), ((a)->hw_addr + E1000_##reg))) : \ + (writel((value), ((a)->hw_addr + E1000_82542_##reg)))) + +#define E1000_READ_REG(a, reg) ( \ + ((a)->mac_type >= e1000_82543) ? \ + readl((a)->hw_addr + E1000_##reg) : \ + readl((a)->hw_addr + E1000_82542_##reg)) + +#define E1000_WRITE_REG_ARRAY(a, reg, offset, value) ( \ + ((a)->mac_type >= e1000_82543) ? \ + writel((value), ((a)->hw_addr + E1000_##reg + ((offset) << 2))) : \ + writel((value), ((a)->hw_addr + E1000_82542_##reg + ((offset) << 2)))) + +#define E1000_READ_REG_ARRAY(a, reg, offset) ( \ + ((a)->mac_type >= e1000_82543) ? \ + readl((a)->hw_addr + E1000_##reg + ((offset) << 2)) : \ + readl((a)->hw_addr + E1000_82542_##reg + ((offset) << 2))) + +#define E1000_WRITE_FLUSH(a) {uint32_t x; x = E1000_READ_REG(a, STATUS);} + +uint32_t +e1000_io_read(struct e1000_hw *hw __unused, uint32_t port) +{ + return inl(port); +} + +void +e1000_io_write(struct e1000_hw *hw __unused, uint32_t port, uint32_t value) +{ + outl(value, port); +} + +static inline void e1000_pci_set_mwi(struct e1000_hw *hw) +{ + pci_write_config_word(hw->pdev, PCI_COMMAND, hw->pci_cmd_word); +} + +static inline void e1000_pci_clear_mwi(struct e1000_hw *hw) +{ + pci_write_config_word(hw->pdev, PCI_COMMAND, + hw->pci_cmd_word & ~PCI_COMMAND_INVALIDATE); +} + +/****************************************************************************** + * 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 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; + + eecd = E1000_READ_REG(hw, EECD); + + /* 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"); + 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) { + + /* Deselect EEPROM */ + 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; + + 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); + } +} + +/****************************************************************************** + * 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; + + /* 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; + + } 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 + *****************************************************************************/ +static int +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"); + + /* 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; + } + + /* Prepare the EEPROM for reading */ + if(e1000_acquire_eeprom(hw) != E1000_SUCCESS) + return -E1000_ERR_EEPROM; + + 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; +} + +/****************************************************************************** + * 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. + *****************************************************************************/ +static int +e1000_validate_eeprom_checksum(struct e1000_hw *hw) +{ + uint16_t checksum = 0; + uint16_t i, eeprom_data; + + DEBUGFUNC("e1000_validate_eeprom_checksum"); + + 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; + } +} + +/****************************************************************************** + * 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 + *****************************************************************************/ +static int +e1000_read_mac_addr(struct e1000_hw *hw) +{ + uint16_t offset; + uint16_t eeprom_data; + int 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->mac_addr[i] = eeprom_data & 0xff; + hw->mac_addr[i+1] = (eeprom_data >> 8) & 0xff; + } + if(((hw->mac_type == e1000_82546) || (hw->mac_type == e1000_82546_rev_3)) && + (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)) + /* Invert the last bit if this is the second device */ + hw->mac_addr[5] ^= 1; + 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 addr_low; + uint32_t addr_high; + + DEBUGFUNC("e1000_init_rx_addrs"); + + /* Setup the receive address. */ + DEBUGOUT("Programming MAC Address into RAR[0]\n"); + addr_low = (hw->mac_addr[0] | + (hw->mac_addr[1] << 8) | + (hw->mac_addr[2] << 16) | (hw->mac_addr[3] << 24)); + + addr_high = (hw->mac_addr[4] | + (hw->mac_addr[5] << 8) | E1000_RAH_AV); + + E1000_WRITE_REG_ARRAY(hw, RA, 0, addr_low); + E1000_WRITE_REG_ARRAY(hw, RA, 1, addr_high); + + /* Zero out the other 15 receive addresses. */ + DEBUGOUT("Clearing RAR[1-15]\n"); + for(i = 1; i < E1000_RAR_ENTRIES; i++) { + E1000_WRITE_REG_ARRAY(hw, RA, (i << 1), 0); + E1000_WRITE_REG_ARRAY(hw, RA, ((i << 1) + 1), 0); + } +} + +/****************************************************************************** + * 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; + + for(offset = 0; offset < E1000_VLAN_FILTER_TBL_SIZE; offset++) + E1000_WRITE_REG_ARRAY(hw, VFTA, offset, 0); +} + +/****************************************************************************** +* 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 +*****************************************************************************/ +void e1000_write_reg_io(struct e1000_hw *hw, uint32_t offset, uint32_t value){ + uint32_t io_addr = hw->io_base; + uint32_t io_data = hw->io_base + 4; + e1000_io_write(hw, io_addr, offset); + e1000_io_write(hw, io_data, value); +} + +/****************************************************************************** + * 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"); + + switch(hw->phy_id) { + case M88E1000_E_PHY_ID: + case M88E1000_I_PHY_ID: + case M88E1011_I_PHY_ID: + hw->phy_type = e1000_phy_m88; + break; + case IGP01E1000_I_PHY_ID: + hw->phy_type = e1000_phy_igp; + break; + 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) +{ + DEBUGFUNC("e1000_phy_init_script"); + +#if 0 + /* See e1000_sw_init() of the Linux driver */ + if(hw->phy_init_script) { +#else + if((hw->mac_type == e1000_82541) || + (hw->mac_type == e1000_82547) || + (hw->mac_type == e1000_82541_rev_2) || + (hw->mac_type == e1000_82547_rev_2)) { +#endif + mdelay(20); + + e1000_write_phy_reg(hw,0x0000,0x0140); + + mdelay(5); + + if(hw->mac_type == e1000_82541 || hw->mac_type == 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); + } else { + e1000_write_phy_reg(hw, 0x1F73, 0x0099); + } + + e1000_write_phy_reg(hw, 0x0000, 0x3300); + + + 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 + *****************************************************************************/ +static int +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: + hw->mac_type = e1000_82546_rev_3; + break; + case E1000_DEV_ID_82541EI: + case E1000_DEV_ID_82541EI_MOBILE: + hw->mac_type = e1000_82541; + break; + case E1000_DEV_ID_82541ER: + case E1000_DEV_ID_82541GI: + case E1000_DEV_ID_82541GI_MOBILE: + hw->mac_type = e1000_82541_rev_2; + break; + case E1000_DEV_ID_82547EI: + hw->mac_type = e1000_82547; + break; + case E1000_DEV_ID_82547GI: + hw->mac_type = e1000_82547_rev_2; + break; + default: + /* Should never have loaded on this device */ + return -E1000_ERR_MAC_TYPE; + } + + return E1000_SUCCESS; +} + +/***************************************************************************** + * Set media type and TBI compatibility. + * + * hw - Struct containing variables accessed by shared code + * **************************************************************************/ +static 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: + hw->media_type = e1000_media_type_internal_serdes; + break; + default: + if(hw->mac_type >= e1000_82543) { + 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; + } + } else { + /* This is an 82542 (fiber only) */ + hw->media_type = e1000_media_type_fiber; + } + } +} + +/****************************************************************************** + * Reset the transmit and receive units; mask and clear all interrupts. + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +static void +e1000_reset_hw(struct e1000_hw *hw) +{ + uint32_t ctrl; + uint32_t ctrl_ext; + uint32_t icr; + uint32_t manc; + + 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); + } + + /* 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 + */ + mdelay(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_IO(hw, CTRL, (ctrl | E1000_CTRL_PHY_RST)); + mdelay(5); + } + + /* 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; + 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 */ + mdelay(2); + break; + case e1000_82541: + case e1000_82541_rev_2: + case e1000_82547: + case e1000_82547_rev_2: + /* Wait for EEPROM reload */ + mdelay(20); + break; + default: + /* Wait for EEPROM reload (it happens automatically) */ + mdelay(5); + break; + } + + /* Disable HW ARPs on ASF enabled adapters */ + if(hw->mac_type >= e1000_82540) { + 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); + } + + /* 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) { +#ifdef LINUX_DRIVER + if(hw->pci_cmd_word & CMD_MEM_WRT_INVALIDATE) +#endif + e1000_pci_set_mwi(hw); + } +} + +/****************************************************************************** + * 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. + *****************************************************************************/ +static int +e1000_init_hw(struct e1000_hw *hw) +{ + uint32_t ctrl, status; + 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; + e1000_bus_type bus_type = e1000_bus_type_unknown; + + DEBUGFUNC("e1000_init_hw"); + + /* Set the media type and TBI compatibility */ + e1000_set_media_type(hw); + + /* Disabling VLAN filtering. */ + DEBUGOUT("Initializing the IEEE VLAN\n"); + 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); + mdelay(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); + mdelay(1); +#ifdef LINUX_DRIVER + if(hw->pci_cmd_word & CMD_MEM_WRT_INVALIDATE) +#endif + e1000_pci_set_mwi(hw); + } + + /* Zero out the Multicast HASH table */ + DEBUGOUT("Zeroing the MTA\n"); + for(i = 0; i < E1000_MC_TBL_SIZE; i++) + E1000_WRITE_REG_ARRAY(hw, MTA, i, 0); + +#if 0 + /* 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. + */ + if(hw->dma_fairness) { + ctrl = E1000_READ_REG(hw, CTRL); + E1000_WRITE_REG(hw, CTRL, ctrl | E1000_CTRL_PRIOR); + } +#endif + + switch(hw->mac_type) { + case e1000_82545_rev_3: + case e1000_82546_rev_3: + break; + default: + if (hw->mac_type >= e1000_82543) { + /* See e1000_get_bus_info() of the Linux driver */ + status = E1000_READ_REG(hw, STATUS); + bus_type = (status & E1000_STATUS_PCIX_MODE) ? + e1000_bus_type_pcix : e1000_bus_type_pci; + } + + /* Workaround for PCI-X problem when BIOS sets MMRBC incorrectly. */ + if(bus_type == e1000_bus_type_pcix) { + pci_read_config_word(hw->pdev, PCIX_COMMAND_REGISTER, &pcix_cmd_word); + pci_read_config_word(hw->pdev, 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; + pci_write_config_word(hw->pdev, PCIX_COMMAND_REGISTER, pcix_cmd_word); + } + } + break; + } + + /* 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 0 + /* 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); +#endif + + 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; + } + + if ((ret_val = e1000_read_eeprom(hw, EEPROM_SERDES_AMPLITUDE, 1, + &eeprom_data))) { + return ret_val; + } + + if(eeprom_data != EEPROM_RESERVED_WORD) { + /* Adjust SERDES output amplitude only. */ + eeprom_data &= EEPROM_SERDES_AMPLITUDE_MASK; + if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_EXT_CTRL, + eeprom_data))) + 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. + *****************************************************************************/ +static int +e1000_setup_link(struct e1000_hw *hw) +{ + uint32_t ctrl_ext; + int32_t ret_val; + uint16_t eeprom_data; + + DEBUGFUNC("e1000_setup_link"); + + /* 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(e1000_read_eeprom(hw, EEPROM_INIT_CONTROL2_REG, 1, &eeprom_data) < 0) { + DEBUGOUT("EEPROM Read Error\n"); + return -E1000_ERR_EEPROM; + } + + if(hw->fc == e1000_fc_default) { + 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; + } + + /* 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 0 + /* See e1000_sw_init() of the Linux driver */ + if((hw->mac_type < e1000_82543) && (hw->report_tx_early == 1)) +#else + if((hw->mac_type < e1000_82543) && (hw->mac_type >= e1000_82543)) +#endif + hw->fc &= (~e1000_fc_rx_pause); + +#if 0 + hw->original_fc = hw->fc; +#endif + + 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) { + 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); + if (ret_val < 0) { + return ret_val; + } + + /* 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"); + + E1000_WRITE_REG(hw, FCAL, FLOW_CONTROL_ADDRESS_LOW); + E1000_WRITE_REG(hw, FCAH, FLOW_CONTROL_ADDRESS_HIGH); + E1000_WRITE_REG(hw, FCT, FLOW_CONTROL_TYPE); +#if 0 + E1000_WRITE_REG(hw, FCTTV, hw->fc_pause_time); +#else + E1000_WRITE_REG(hw, FCTTV, FC_DEFAULT_TX_TIMER); +#endif + + /* 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 0 + 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); + } +#else + E1000_WRITE_REG(hw, FCRTL, (FC_DEFAULT_LO_THRESH | E1000_FCRTL_XONE)); + E1000_WRITE_REG(hw, FCRTH, FC_DEFAULT_HI_THRESH); +#endif + } + 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 int +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 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 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; + + if((ret_val = e1000_adjust_serdes_amplitude(hw))) + return ret_val; + + /* Take the link out of reset */ + ctrl &= ~(E1000_CTRL_LRST); + +#if 0 + /* Adjust VCO speed to improve BER performance */ + if((ret_val = e1000_set_vco_speed(hw))) + return ret_val; +#endif + + 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; + mdelay(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++) { + mdelay(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. + */ + if((ret_val = e1000_check_for_link(hw))) { + 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; +} + +/****************************************************************************** +* Detects which PHY is present and the speed and duplex +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +static int +e1000_setup_copper_link(struct e1000_hw *hw) +{ + uint32_t ctrl; + int32_t ret_val; + uint16_t i; + uint16_t phy_data; + + DEBUGFUNC("e1000_setup_copper_link"); + + 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); + e1000_phy_hw_reset(hw); + } + + /* Make sure we have a valid PHY */ + if((ret_val = e1000_detect_gig_phy(hw))) { + DEBUGOUT("Error, did not detect valid phy.\n"); + return ret_val; + } + DEBUGOUT1("Phy ID = %x \n", hw->phy_id); + + if(hw->mac_type <= e1000_82543 || + hw->mac_type == e1000_82541 || hw->mac_type == e1000_82547 || +#if 0 + hw->mac_type == e1000_82541_rev_2 || hw->mac_type == e1000_82547_rev_2) + hw->phy_reset_disable = FALSE; + + if(!hw->phy_reset_disable) { +#else + hw->mac_type == e1000_82541_rev_2 || hw->mac_type == e1000_82547_rev_2) { +#endif + if (hw->phy_type == e1000_phy_igp) { + + if((ret_val = e1000_phy_reset(hw))) { + DEBUGOUT("Error Resetting the PHY\n"); + return ret_val; + } + + /* Wait 10ms for MAC to configure PHY from eeprom settings */ + mdelay(15); + +#if 0 + /* disable lplu d3 during driver init */ + if((ret_val = e1000_set_d3_lplu_state(hw, FALSE))) { + DEBUGOUT("Error Disabling LPLU D3\n"); + return ret_val; + } + + /* Configure mdi-mdix settings */ + if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, + &phy_data))) + 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 IGP B-0 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; + } + } + if((ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, + phy_data))) + return ret_val; + + /* set auto-master slave resolution settings */ + 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; +#endif + + /* 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 */ + if((ret_val = e1000_read_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + &phy_data))) + return ret_val; + phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED; + if((ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + phy_data))) + return ret_val; + /* Set auto Master/Slave resolution process */ + if((ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL, + &phy_data))) + return ret_val; + phy_data &= ~CR_1000T_MS_ENABLE; + if((ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, + phy_data))) + return ret_val; + } + + if((ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL, + &phy_data))) + return ret_val; + +#if 0 + /* 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; + } +#endif + + if((ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, + phy_data))) + return ret_val; + } else { + /* Enable CRS on TX. This must be set for half-duplex operation. */ + if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, + &phy_data))) + 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) + */ +#if 0 + 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: +#endif + phy_data |= M88E1000_PSCR_AUTO_X_MODE; +#if 0 + break; + } +#endif + + /* Options: + * disable_polarity_correction = 0 (default) + * Automatic Correction for Reversed Cable Polarity + * 0 - Disabled + * 1 - Enabled + */ + phy_data &= ~M88E1000_PSCR_POLARITY_REVERSAL; + if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, + phy_data))) + return ret_val; + + /* Force TX_CLK in the Extended PHY Specific Control Register + * to 25MHz clock. + */ + if((ret_val = e1000_read_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, + &phy_data))) + return ret_val; + + phy_data |= M88E1000_EPSCR_TX_CLK_25; + +#ifdef LINUX_DRIVER + if (hw->phy_revision < M88E1011_I_REV_4) { +#endif + /* 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); + if((ret_val = e1000_write_phy_reg(hw, + M88E1000_EXT_PHY_SPEC_CTRL, + phy_data))) + return ret_val; + } + + /* SW Reset the PHY so all changes take effect */ + if((ret_val = e1000_phy_reset(hw))) { + DEBUGOUT("Error Resetting the PHY\n"); + return ret_val; +#ifdef LINUX_DRIVER + } +#endif + } + + /* Options: + * autoneg = 1 (default) + * PHY will advertise value(s) parsed from + * autoneg_advertised and fc + * autoneg = 0 + * PHY will be set to 10H, 10F, 100H, or 100F + * depending on value parsed from forced_speed_duplex. + */ + + /* Is autoneg enabled? This is enabled by default or by software + * override. If so, call e1000_phy_setup_autoneg routine to parse the + * autoneg_advertised and fc options. If autoneg is NOT enabled, then + * the user should have provided a speed/duplex override. If so, then + * call e1000_phy_force_speed_duplex to parse and set this up. + */ + /* 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; + + DEBUGOUT("Reconfiguring auto-neg advertisement params\n"); + if((ret_val = e1000_phy_setup_autoneg(hw))) { + 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. + */ + if((ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &phy_data))) + return ret_val; + + phy_data |= (MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG); + if((ret_val = e1000_write_phy_reg(hw, PHY_CTRL, phy_data))) + return ret_val; + +#if 0 + /* 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) { + if((ret_val = e1000_wait_autoneg(hw))) { + DEBUGOUT("Error while waiting for autoneg to complete\n"); + return ret_val; + } + } +#else + /* If we do not wait for autonegotiation to complete I + * do not see a valid link status. + */ + if((ret_val = e1000_wait_autoneg(hw))) { + DEBUGOUT("Error while waiting for autoneg to complete\n"); + return ret_val; + } +#endif + } /* !hw->phy_reset_disable */ + + /* Check link status. Wait up to 100 microseconds for link to become + * valid. + */ + for(i = 0; i < 10; i++) { + if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data))) + return ret_val; + if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data))) + return ret_val; + + if(phy_data & MII_SR_LINK_STATUS) { + /* We have link, so we need to finish the config process: + * 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. + */ + if(hw->mac_type >= e1000_82544) { + e1000_config_collision_dist(hw); + } else { + if((ret_val = e1000_config_mac_to_phy(hw))) { + DEBUGOUT("Error configuring MAC to PHY settings\n"); + return ret_val; + } + } + if((ret_val = e1000_config_fc_after_link_up(hw))) { + DEBUGOUT("Error Configuring Flow Control\n"); + return ret_val; + } +#if 0 + if(hw->phy_type == e1000_phy_igp) { + if((ret_val = e1000_config_dsp_after_link_change(hw, TRUE))) { + DEBUGOUT("Error Configuring DSP after link up\n"); + return ret_val; + } + } +#endif + DEBUGOUT("Valid link established!!!\n"); + return E1000_SUCCESS; + } + udelay(10); + } + + DEBUGOUT("Unable to establish link!!!\n"); + return -E1000_ERR_NOLINK; +} + +/****************************************************************************** +* Configures PHY autoneg and flow control advertisement settings +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +static int +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). */ + if((ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_ADV, + &mii_autoneg_adv_reg))) + return ret_val; + + /* Read the MII 1000Base-T Control Register (Address 9). */ + if((ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL, &mii_1000t_ctrl_reg))) + return ret_val; + + /* 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; + } + + /* 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; + } + + if((ret_val = e1000_write_phy_reg(hw, PHY_AUTONEG_ADV, + mii_autoneg_adv_reg))) + return ret_val; + + DEBUGOUT1("Auto-Neg Advertising %x\n", mii_autoneg_adv_reg); + + if((ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, mii_1000t_ctrl_reg))) + 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. +******************************************************************************/ +static void +e1000_config_collision_dist(struct e1000_hw *hw) +{ + uint32_t tctl; + + tctl = E1000_READ_REG(hw, TCTL); + + tctl &= ~E1000_TCTL_COLD; + tctl |= E1000_COLLISION_DISTANCE << 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 int +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"); + + /* 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. + */ + if (hw->phy_type == e1000_phy_igp) { + if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_STATUS, + &phy_data))) + return ret_val; + + if(phy_data & IGP01E1000_PSSR_FULL_DUPLEX) 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 & IGP01E1000_PSSR_SPEED_MASK) == + IGP01E1000_PSSR_SPEED_1000MBPS) + ctrl |= E1000_CTRL_SPD_1000; + else if((phy_data & IGP01E1000_PSSR_SPEED_MASK) == + IGP01E1000_PSSR_SPEED_100MBPS) + ctrl |= E1000_CTRL_SPD_100; + } else { + if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, + &phy_data))) + 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. + *****************************************************************************/ +static int +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 int +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))) { + if((ret_val = e1000_force_mac_fc(hw))) { + 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) { + /* 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. + */ + if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg))) + return ret_val; + if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg))) + 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. + */ + if((ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_ADV, + &mii_nway_adv_reg))) + return ret_val; + if((ret_val = e1000_read_phy_reg(hw, PHY_LP_ABILITY, + &mii_nway_lp_ability_reg))) + 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 0 + if(hw->original_fc == e1000_fc_full) { + hw->fc = e1000_fc_full; +#else + if(hw->fc == e1000_fc_full) { +#endif + DEBUGOUT("Flow Control = FULL.\r\n"); + } else { + hw->fc = e1000_fc_rx_pause; + DEBUGOUT("Flow Control = RX PAUSE frames only.\r\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.\r\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.\r\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. + */ +#if 0 + else if(hw->original_fc == e1000_fc_none || + hw->original_fc == e1000_fc_tx_pause) { +#else + else if(hw->fc == e1000_fc_none) + DEBUGOUT("Flow Control = NONE.\r\n"); + else if(hw->fc == e1000_fc_tx_pause) { +#endif + hw->fc = e1000_fc_none; + DEBUGOUT("Flow Control = NONE.\r\n"); + } else { + hw->fc = e1000_fc_rx_pause; + DEBUGOUT("Flow Control = RX PAUSE frames only.\r\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. + */ + e1000_get_speed_and_duplex(hw, &speed, &duplex); + + 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. + */ + if((ret_val = e1000_force_mac_fc(hw))) { + DEBUGOUT("Error forcing flow control settings\n"); + return ret_val; + } + } else { + DEBUGOUT("Copper PHY and Auto Neg has not completed.\r\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. + *****************************************************************************/ +static int +e1000_check_for_link(struct e1000_hw *hw) +{ + uint32_t rxcw; + uint32_t ctrl; + uint32_t status; + uint32_t rctl; + uint32_t signal = 0; + int32_t ret_val; + uint16_t phy_data; + uint16_t lp_capability; + + DEBUGFUNC("e1000_check_for_link"); + + /* 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) + signal = (hw->mac_type > e1000_82544) ? E1000_CTRL_SWDPIN1 : 0; + + ctrl = E1000_READ_REG(hw, CTRL); + status = E1000_READ_REG(hw, STATUS); + rxcw = E1000_READ_REG(hw, RXCW); + + /* 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 0 + if((hw->media_type == e1000_media_type_copper) && hw->get_link_status) { +#else + if(hw->media_type == e1000_media_type_copper) { +#endif + /* 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. + */ + if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data))) + return ret_val; + if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data))) + return ret_val; + + if(phy_data & MII_SR_LINK_STATUS) { +#if 0 + hw->get_link_status = FALSE; +#endif + } else { + /* No link detected */ + return -E1000_ERR_NOLINK; + } + + /* 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 { + if((ret_val = e1000_config_mac_to_phy(hw))) { + 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. + */ + if((ret_val = e1000_config_fc_after_link_up(hw))) { + 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 + * parter capability register. We use the link partner capability to + * determine if TBI Compatibility needs to be turned on or off. If + * the link partner advertises any speed in addition to Gigabit, then + * we assume that they are GMII-based, and TBI compatibility is not + * needed. If no other speeds are advertised, we assume the link + * partner is TBI-based, and we turn on TBI Compatibility. + */ + if(hw->tbi_compatibility_en) { + if((ret_val = e1000_read_phy_reg(hw, PHY_LP_ABILITY, + &lp_capability))) + return ret_val; + if(lp_capability & (NWAY_LPAR_10T_HD_CAPS | + NWAY_LPAR_10T_FD_CAPS | + NWAY_LPAR_100TX_HD_CAPS | + NWAY_LPAR_100TX_FD_CAPS | + NWAY_LPAR_100T4_CAPS)) { + /* If our link partner advertises anything in addition to + * gigabit, 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.\r\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. */ + if((ret_val = e1000_config_fc_after_link_up(hw))) { + 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.\r\n"); + E1000_WRITE_REG(hw, TXCW, hw->txcw); + E1000_WRITE_REG(hw, CTRL, (ctrl & ~E1000_CTRL_SLU)); + } +#if 0 + /* 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"); + } + } +#endif + 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 + *****************************************************************************/ +static void +e1000_get_speed_and_duplex(struct e1000_hw *hw, + uint16_t *speed, + uint16_t *duplex) +{ + uint32_t status; + + 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\r\n"); + } else { + *duplex = HALF_DUPLEX; + DEBUGOUT(" Half Duplex\r\n"); + } + } else { + DEBUGOUT("1000 Mbs, Full Duplex\r\n"); + *speed = SPEED_1000; + *duplex = FULL_DUPLEX; + } +} + +/****************************************************************************** +* Blocks until autoneg completes or times out (~4.5 seconds) +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +static int +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. + */ + if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data))) + return ret_val; + if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data))) + return ret_val; + if(phy_data & MII_SR_AUTONEG_COMPLETE) { + DEBUGOUT("Auto-Neg complete.\n"); + return E1000_SUCCESS; + } + mdelay(100); + } + DEBUGOUT("Auto-Neg timedout.\n"); + return -E1000_ERR_TIMEOUT; +} + +/****************************************************************************** +* 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; +} + +/***************************************************************************** +* 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 +******************************************************************************/ +static int +e1000_read_phy_reg(struct e1000_hw *hw, + uint32_t reg_addr, + uint16_t *phy_data) +{ + uint32_t ret_val; + + DEBUGFUNC("e1000_read_phy_reg"); + + if(hw->phy_type == e1000_phy_igp && + (reg_addr > MAX_PHY_MULTI_PAGE_REG)) { + if((ret_val = e1000_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT, + (uint16_t)reg_addr))) + return ret_val; + } + + ret_val = e1000_read_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT & reg_addr, + phy_data); + + return ret_val; +} + +static int +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 %d 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: + * + * 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 +******************************************************************************/ +static int +e1000_write_phy_reg(struct e1000_hw *hw, + uint32_t reg_addr, + uint16_t phy_data) +{ + uint32_t ret_val; + + DEBUGFUNC("e1000_write_phy_reg"); + + if(hw->phy_type == e1000_phy_igp && + (reg_addr > MAX_PHY_MULTI_PAGE_REG)) { + if((ret_val = e1000_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT, + (uint16_t)reg_addr))) + return ret_val; + } + + ret_val = e1000_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT & reg_addr, + phy_data); + + return ret_val; +} + +static int +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 %d 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 < 640; 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: + * . + */ + 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; +} + +/****************************************************************************** +* Returns the PHY to the power-on reset state +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +static void +e1000_phy_hw_reset(struct e1000_hw *hw) +{ + uint32_t ctrl, ctrl_ext; + + DEBUGFUNC("e1000_phy_hw_reset"); + + DEBUGOUT("Resetting Phy...\n"); + + if(hw->mac_type > e1000_82543) { + /* Read the device control register and assert the E1000_CTRL_PHY_RST + * bit. Then, take it out of reset. + */ + ctrl = E1000_READ_REG(hw, CTRL); + E1000_WRITE_REG(hw, CTRL, ctrl | E1000_CTRL_PHY_RST); + E1000_WRITE_FLUSH(hw); + mdelay(10); + E1000_WRITE_REG(hw, CTRL, ctrl); + E1000_WRITE_FLUSH(hw); + } 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); + mdelay(10); + ctrl_ext |= E1000_CTRL_EXT_SDP4_DATA; + E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); + E1000_WRITE_FLUSH(hw); + } + udelay(150); +} + +/****************************************************************************** +* Resets the PHY +* +* hw - Struct containing variables accessed by shared code +* +* Sets bit 15 of the MII Control regiser +******************************************************************************/ +static int +e1000_phy_reset(struct e1000_hw *hw) +{ + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC("e1000_phy_reset"); + + if(hw->mac_type != e1000_82541_rev_2) { + if((ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &phy_data))) + return ret_val; + + phy_data |= MII_CR_RESET; + if((ret_val = e1000_write_phy_reg(hw, PHY_CTRL, phy_data))) + return ret_val; + + udelay(1); + } else e1000_phy_hw_reset(hw); + + if(hw->phy_type == e1000_phy_igp) + e1000_phy_init_script(hw); + + return E1000_SUCCESS; +} + +/****************************************************************************** +* Probes the expected PHY address for known PHY IDs +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +static int +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"); + + /* Read the PHY ID Registers to identify which PHY is onboard. */ + if((ret_val = e1000_read_phy_reg(hw, PHY_ID1, &phy_id_high))) + return ret_val; + + hw->phy_id = (uint32_t) (phy_id_high << 16); + udelay(20); + if((ret_val = e1000_read_phy_reg(hw, PHY_ID2, &phy_id_low))) + return ret_val; + + hw->phy_id |= (uint32_t) (phy_id_low & PHY_REVISION_MASK); +#ifdef LINUX_DRIVER + hw->phy_revision = (uint32_t) phy_id_low & ~PHY_REVISION_MASK; +#endif + + 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; + 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 0x%X detected\n", hw->phy_id); + return E1000_SUCCESS; + } + DEBUGOUT1("Invalid PHY ID 0x%X\n", hw->phy_id); + return -E1000_ERR_PHY; +} + +/****************************************************************************** + * Sets up eeprom variables in the hw struct. Must be called after mac_type + * is configured. + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +static void +e1000_init_eeprom_params(struct e1000_hw *hw) +{ + struct e1000_eeprom_info *eeprom = &hw->eeprom; + uint32_t eecd = E1000_READ_REG(hw, EECD); + 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; + 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; + } + 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; + 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; + } + } + break; + default: + eeprom->type = e1000_eeprom_spi; + if (eecd & E1000_EECD_ADDR_BITS) { + eeprom->page_size = 32; + eeprom->address_bits = 16; + } else { + eeprom->page_size = 8; + eeprom->address_bits = 8; + } + break; + } + + if (eeprom->type == e1000_eeprom_spi) { + eeprom->opcode_bits = 8; + eeprom->delay_usec = 1; + eeprom->word_size = 64; + if (e1000_read_eeprom(hw, EEPROM_CFG, 1, &eeprom_size) == 0) { + eeprom_size &= EEPROM_SIZE_MASK; + + switch (eeprom_size) { + case EEPROM_SIZE_16KB: + eeprom->word_size = 8192; + break; + case EEPROM_SIZE_8KB: + eeprom->word_size = 4096; + break; + case EEPROM_SIZE_4KB: + eeprom->word_size = 2048; + break; + case EEPROM_SIZE_2KB: + eeprom->word_size = 1024; + break; + case EEPROM_SIZE_1KB: + eeprom->word_size = 512; + break; + case EEPROM_SIZE_512B: + eeprom->word_size = 256; + break; + case EEPROM_SIZE_128B: + default: + break; + } + } + } +} + +/** + * e1000_reset - Reset the adapter + */ + +static int +e1000_reset(struct e1000_hw *hw) +{ + uint32_t pba; + /* Repartition Pba for greater than 9k mtu + * To take effect CTRL.RST is required. + */ + + if(hw->mac_type < e1000_82547) { + pba = E1000_PBA_48K; + } else { + pba = E1000_PBA_30K; + } + E1000_WRITE_REG(hw, PBA, pba); + + /* flow control settings */ +#if 0 + hw->fc_high_water = FC_DEFAULT_HI_THRESH; + hw->fc_low_water = FC_DEFAULT_LO_THRESH; + hw->fc_pause_time = FC_DEFAULT_TX_TIMER; + hw->fc_send_xon = 1; + hw->fc = hw->original_fc; +#endif + + e1000_reset_hw(hw); + if(hw->mac_type >= e1000_82544) + E1000_WRITE_REG(hw, WUC, 0); + return e1000_init_hw(hw); +} + +/** + * e1000_sw_init - Initialize general software structures (struct e1000_adapter) + * @adapter: board private structure to initialize + * + * 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 pci_device *pdev, struct e1000_hw *hw) +{ + int result; + + /* PCI config space info */ + pci_read_config_word(pdev, PCI_VENDOR_ID, &hw->vendor_id); + pci_read_config_word(pdev, PCI_DEVICE_ID, &hw->device_id); + pci_read_config_byte(pdev, PCI_REVISION, &hw->revision_id); +#if 0 + pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, + &hw->subsystem_vendor_id); + pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &hw->subsystem_id); +#endif + + pci_read_config_word(pdev, PCI_COMMAND, &hw->pci_cmd_word); + + /* identify the MAC */ + + result = e1000_set_mac_type(hw); + if (result) { + E1000_ERR("Unknown MAC Type\n"); + return result; + } + + /* initialize eeprom parameters */ + + e1000_init_eeprom_params(hw); + +#if 0 + if((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_init_script = 1; +#endif + + e1000_set_media_type(hw); + +#if 0 + if(hw->mac_type < e1000_82543) + hw->report_tx_early = 0; + else + hw->report_tx_early = 1; + + hw->wait_autoneg_complete = FALSE; +#endif + hw->tbi_compatibility_en = TRUE; +#if 0 + 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; + } +#endif + return E1000_SUCCESS; +} + +static void fill_rx (void) +{ + struct e1000_rx_desc *rd; + rx_last = rx_tail; + rd = rx_base + rx_tail; + rx_tail = (rx_tail + 1) % 8; + memset (rd, 0, 16); + rd->buffer_addr = virt_to_bus(&packet); + E1000_WRITE_REG (&hw, RDT, rx_tail); +} + +static void init_descriptor (void) +{ + unsigned long ptr; + unsigned long tctl; + + ptr = virt_to_phys(tx_pool); + if (ptr & 0xf) + ptr = (ptr + 0x10) & (~0xf); + + tx_base = phys_to_virt(ptr); + + E1000_WRITE_REG (&hw, TDBAL, virt_to_bus(tx_base)); + E1000_WRITE_REG (&hw, TDBAH, 0); + E1000_WRITE_REG (&hw, TDLEN, 128); + + /* Setup the HW Tx Head and Tail descriptor pointers */ + + E1000_WRITE_REG (&hw, TDH, 0); + E1000_WRITE_REG (&hw, TDT, 0); + tx_tail = 0; + + /* Program the Transmit Control Register */ + +#ifdef LINUX_DRIVER_TCTL + tctl = E1000_READ_REG(&hw, TCTL); + + tctl &= ~E1000_TCTL_CT; + tctl |= E1000_TCTL_EN | E1000_TCTL_PSP | + (E1000_COLLISION_THRESHOLD << E1000_CT_SHIFT); +#else + tctl = E1000_TCTL_PSP | E1000_TCTL_EN | + (E1000_COLLISION_THRESHOLD << E1000_CT_SHIFT) | + (E1000_HDX_COLLISION_DISTANCE << E1000_COLD_SHIFT); +#endif + + E1000_WRITE_REG (&hw, TCTL, tctl); + + e1000_config_collision_dist(&hw); + + + rx_tail = 0; + /* disable receive */ + E1000_WRITE_REG (&hw, RCTL, 0); + ptr = virt_to_phys(rx_pool); + if (ptr & 0xf) + ptr = (ptr + 0x10) & (~0xf); + rx_base = phys_to_virt(ptr); + + /* Setup the Base and Length of the Rx Descriptor Ring */ + + E1000_WRITE_REG (&hw, RDBAL, virt_to_bus(rx_base)); + E1000_WRITE_REG (&hw, RDBAH, 0); + + E1000_WRITE_REG (&hw, RDLEN, 128); + + /* Setup the HW Rx Head and Tail Descriptor Pointers */ + E1000_WRITE_REG (&hw, RDH, 0); + E1000_WRITE_REG (&hw, RDT, 0); + + E1000_WRITE_REG (&hw, RCTL, + E1000_RCTL_EN | + E1000_RCTL_BAM | + E1000_RCTL_SZ_2048 | + E1000_RCTL_MPE); + fill_rx(); +} + + + +/************************************************************************** +POLL - Wait for a frame +***************************************************************************/ +static int +e1000_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 e1000_rx_desc *rd; + uint32_t icr; + + rd = rx_base + rx_last; + if (!rd->status & E1000_RXD_STAT_DD) + return 0; + + if ( ! retrieve ) return 1; + + // printf("recv: packet %! -> %! len=%d \n", packet+6, packet,rd->Length); + memcpy (nic->packet, packet, rd->length); + nic->packetlen = rd->length; + fill_rx (); + + /* Acknowledge interrupt. */ + icr = E1000_READ_REG(&hw, ICR); + + return 1; +} + +/************************************************************************** +TRANSMIT - Transmit a frame +***************************************************************************/ +static void +e1000_transmit (struct nic *nic, const char *d, /* Destination */ + unsigned int type, /* Type */ + unsigned int size, /* size */ + const char *p) /* Packet */ +{ + /* send the packet to destination */ + struct eth_hdr { + unsigned char dst_addr[ETH_ALEN]; + unsigned char src_addr[ETH_ALEN]; + unsigned short type; + } hdr; + struct e1000_tx_desc *txhd; /* header */ + struct e1000_tx_desc *txp; /* payload */ + DEBUGFUNC("send"); + + memcpy (&hdr.dst_addr, d, ETH_ALEN); + memcpy (&hdr.src_addr, nic->node_addr, ETH_ALEN); + + hdr.type = htons (type); + txhd = tx_base + tx_tail; + tx_tail = (tx_tail + 1) % 8; + txp = tx_base + tx_tail; + tx_tail = (tx_tail + 1) % 8; + + txhd->buffer_addr = virt_to_bus (&hdr); + txhd->lower.data = sizeof (hdr); + txhd->upper.data = 0; + + txp->buffer_addr = virt_to_bus(p); + txp->lower.data = E1000_TXD_CMD_RPS | E1000_TXD_CMD_EOP | E1000_TXD_CMD_IFCS | size; + txp->upper.data = 0; + + E1000_WRITE_REG (&hw, TDT, tx_tail); + while (!(txp->upper.data & E1000_TXD_STAT_DD)) { + udelay(10); /* give the nic a chance to write to the register */ + poll_interruptions(); + } + DEBUGFUNC("send end"); +} + + +/************************************************************************** +DISABLE - Turn off ethernet interface +***************************************************************************/ +static void e1000_disable (struct dev *dev __unused) +{ + /* Clear the transmit ring */ + E1000_WRITE_REG (&hw, TDH, 0); + E1000_WRITE_REG (&hw, TDT, 0); + + /* Clear the receive ring */ + E1000_WRITE_REG (&hw, RDH, 0); + E1000_WRITE_REG (&hw, RDT, 0); + + /* put the card in its initial state */ + 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, 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, E1000_CTRL_RST); + break; + default: + E1000_WRITE_REG(&hw, CTRL, E1000_CTRL_RST); + break; + } + + /* Turn off the ethernet interface */ + E1000_WRITE_REG (&hw, RCTL, 0); + E1000_WRITE_REG (&hw, TCTL, 0); + mdelay (10); + + /* Unmap my window to the device */ + iounmap(hw.hw_addr); +} + +/************************************************************************** +IRQ - Enable, Disable, or Force interrupts +***************************************************************************/ +static void e1000_irq(struct nic *nic __unused, irq_action_t action) +{ + switch ( action ) { + case DISABLE : + E1000_WRITE_REG(&hw, IMC, ~0); + E1000_WRITE_FLUSH(&hw); + break; + case ENABLE : + E1000_WRITE_REG(&hw, IMS, + E1000_IMS_RXT0 | E1000_IMS_RXSEQ); + E1000_WRITE_FLUSH(&hw); + break; + case FORCE : + E1000_WRITE_REG(&hw, ICS, E1000_ICS_RXT0); + break; + } +} + +#define IORESOURCE_IO 0x00000100 /* Resource type */ +#define BAR_0 0 +#define BAR_1 1 +#define BAR_5 5 + +/************************************************************************** +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 e1000_probe(struct dev *dev, struct pci_device *p) +{ + struct nic *nic = (struct nic *)dev; + unsigned long mmio_start, mmio_len; + int ret_val, i; + + if (p == 0) + return 0; + /* Initialize hw with default values */ + memset(&hw, 0, sizeof(hw)); + hw.pdev = p; + +#if 1 + /* Are these variables needed? */ + hw.fc = e1000_fc_none; +#if 0 + hw.original_fc = e1000_fc_none; +#endif + hw.autoneg_failed = 0; +#if 0 + hw.get_link_status = TRUE; +#endif +#endif + + mmio_start = pci_bar_start(p, PCI_BASE_ADDRESS_0); + mmio_len = pci_bar_size(p, PCI_BASE_ADDRESS_0); + hw.hw_addr = ioremap(mmio_start, mmio_len); + + for(i = BAR_1; i <= BAR_5; i++) { + if(pci_bar_size(p, i) == 0) + continue; + if(pci_find_capability(p, i) & IORESOURCE_IO) { + hw.io_base = pci_bar_start(p, i); + break; + } + } + + adjust_pci_device(p); + + nic->ioaddr = p->ioaddr & ~3; + nic->irqno = p->irq; + + /* From Matt Hortman */ + /* MAC and Phy settings */ + + /* setup the private structure */ + if (e1000_sw_init(p, &hw) < 0) { + iounmap(hw.hw_addr); + return 0; + } + + /* make sure the EEPROM is good */ + + if (e1000_validate_eeprom_checksum(&hw) < 0) { + printf ("The EEPROM Checksum Is Not Valid\n"); + iounmap(hw.hw_addr); + return 0; + } + + /* copy the MAC address out of the EEPROM */ + + e1000_read_mac_addr(&hw); + memcpy (nic->node_addr, hw.mac_addr, ETH_ALEN); + + printf("Ethernet addr: %!\n", nic->node_addr); + + /* reset the hardware with the new settings */ + + ret_val = e1000_reset(&hw); + if (ret_val < 0) { + if ((ret_val == -E1000_ERR_NOLINK) || + (ret_val == -E1000_ERR_TIMEOUT)) { + E1000_ERR("Valid Link not detected\n"); + } else { + E1000_ERR("Hardware Initialization Failed\n"); + } + iounmap(hw.hw_addr); + return 0; + } + init_descriptor(); + + /* point to NIC specific routines */ + dev->disable = e1000_disable; + nic->poll = e1000_poll; + nic->transmit = e1000_transmit; + nic->irq = e1000_irq; + + return 1; +} + +static struct pci_id e1000_nics[] = { +PCI_ROM(0x8086, 0x1000, "e1000-82542", "Intel EtherExpressPro1000"), +PCI_ROM(0x8086, 0x1001, "e1000-82543gc-fiber", "Intel EtherExpressPro1000 82543GC Fiber"), +PCI_ROM(0x8086, 0x1004, "e1000-82543gc-copper", "Intel EtherExpressPro1000 82543GC Copper"), +PCI_ROM(0x8086, 0x1008, "e1000-82544ei-copper", "Intel EtherExpressPro1000 82544EI Copper"), +PCI_ROM(0x8086, 0x1009, "e1000-82544ei-fiber", "Intel EtherExpressPro1000 82544EI Fiber"), +PCI_ROM(0x8086, 0x100C, "e1000-82544gc-copper", "Intel EtherExpressPro1000 82544GC Copper"), +PCI_ROM(0x8086, 0x100D, "e1000-82544gc-lom", "Intel EtherExpressPro1000 82544GC LOM"), +PCI_ROM(0x8086, 0x100E, "e1000-82540em", "Intel EtherExpressPro1000 82540EM"), +PCI_ROM(0x8086, 0x100F, "e1000-82545em-copper", "Intel EtherExpressPro1000 82545EM Copper"), +PCI_ROM(0x8086, 0x1010, "e1000-82546eb-copper", "Intel EtherExpressPro1000 82546EB Copper"), +PCI_ROM(0x8086, 0x1011, "e1000-82545em-fiber", "Intel EtherExpressPro1000 82545EM Fiber"), +PCI_ROM(0x8086, 0x1012, "e1000-82546eb-fiber", "Intel EtherExpressPro1000 82546EB Copper"), +PCI_ROM(0x8086, 0x1013, "e1000-82541ei", "Intel EtherExpressPro1000 82541EI"), +PCI_ROM(0x8086, 0x1015, "e1000-82540em-lom", "Intel EtherExpressPro1000 82540EM LOM"), +PCI_ROM(0x8086, 0x1016, "e1000-82540ep-lom", "Intel EtherExpressPro1000 82540EP LOM"), +PCI_ROM(0x8086, 0x1017, "e1000-82540ep", "Intel EtherExpressPro1000 82540EP"), +PCI_ROM(0x8086, 0x1018, "e1000-82541ep", "Intel EtherExpressPro1000 82541EP"), +PCI_ROM(0x8086, 0x1019, "e1000-82547ei", "Intel EtherExpressPro1000 82547EI"), +PCI_ROM(0x8086, 0x101d, "e1000-82546eb-quad-copper", "Intel EtherExpressPro1000 82546EB Quad Copper"), +PCI_ROM(0x8086, 0x101e, "e1000-82540ep-lp", "Intel EtherExpressPro1000 82540EP LP"), +PCI_ROM(0x8086, 0x1026, "e1000-82545gm-copper", "Intel EtherExpressPro1000 82545GM Copper"), +PCI_ROM(0x8086, 0x1027, "e1000-82545gm-fiber", "Intel EtherExpressPro1000 82545GM Fiber"), +PCI_ROM(0x8086, 0x1028, "e1000-82545gm-serdes", "Intel EtherExpressPro1000 82545GM SERDES"), +PCI_ROM(0x8086, 0x1075, "e1000-82547gi", "Intel EtherExpressPro1000 82547GI"), +PCI_ROM(0x8086, 0x1076, "e1000-82541gi", "Intel EtherExpressPro1000 82541GI"), +PCI_ROM(0x8086, 0x1077, "e1000-82541gi-mobile", "Intel EtherExpressPro1000 82541GI Mobile"), +PCI_ROM(0x8086, 0x1078, "e1000-82541er", "Intel EtherExpressPro1000 82541ER"), +PCI_ROM(0x8086, 0x1079, "e1000-82546gb-copper", "Intel EtherExpressPro1000 82546GB Copper"), +PCI_ROM(0x8086, 0x107a, "e1000-82546gb-fiber", "Intel EtherExpressPro1000 82546GB Fiber"), +PCI_ROM(0x8086, 0x107b, "e1000-82546gb-serdes", "Intel EtherExpressPro1000 82546GB SERDES"), +}; + +static struct pci_driver e1000_driver __pci_driver = { + .type = NIC_DRIVER, + .name = "E1000", + .probe = e1000_probe, + .ids = e1000_nics, + .id_count = sizeof(e1000_nics)/sizeof(e1000_nics[0]), + .class = 0, +}; diff --git a/src/drivers/net/e1000_hw.h b/src/drivers/net/e1000_hw.h new file mode 100644 index 00000000..7c7f48f6 --- /dev/null +++ b/src/drivers/net/e1000_hw.h @@ -0,0 +1,2058 @@ +/******************************************************************************* + + + Copyright(c) 1999 - 2003 Intel Corporation. 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. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Contact Information: + Linux NICS + 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_ + +/* 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_num_macs +} e1000_mac_type; + +typedef enum { + e1000_eeprom_uninitialized = 0, + e1000_eeprom_spi, + e1000_eeprom_microwire, + 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; + +/* PCI bus types */ +typedef enum { + e1000_bus_type_unknown = 0, + e1000_bus_type_pci, + e1000_bus_type_pcix, + 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_reserved +} e1000_bus_speed; + +/* PCI bus widths */ +typedef enum { + e1000_bus_width_unknown = 0, + 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_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_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_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_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; +}; + + + +/* 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_NOLINK 7 +#define E1000_ERR_TIMEOUT 8 + +#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 0x1078 +#define E1000_DEV_ID_82547GI 0x1075 +#define E1000_DEV_ID_82541GI 0x1076 +#define E1000_DEV_ID_82541GI_MOBILE 0x1077 +#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_82547EI 0x1019 + +#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 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) + +/* 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 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 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_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_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 0x000D /* Priority is in upper 3 of 16 */ +#define E1000_RXD_SPC_CFI_MASK 0x1000 /* CFI is bit 12 */ +#define E1000_RXD_SPC_CFI_SHIFT 0x000C /* CFI is bit 12 */ + +/* 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) + +/* 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) */ + + +/* 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 + +/* 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_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 + +/* 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_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_RCTL 0x00100 /* RX Control - 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_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_PBA 0x01000 /* Packet Buffer Allocation - RW */ +#define E1000_FCRTL 0x02160 /* Flow Control Receive Threshold Low - RW */ +#define E1000_FCRTH 0x02168 /* Flow Control Receive Threshold High - 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_RXDCTL 0x02828 /* RX Descriptor Control - RW */ +#define E1000_RADV 0x0282C /* RX Interrupt Absolute Delay Timer - RW */ +#define E1000_RSRPD 0x02C00 /* RX Small Packet Detect - RW */ +#define E1000_TXDMAC 0x03000 /* TX DMA Control - RW */ +#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_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_RXCSUM 0x05000 /* RX Checksum Control - RW */ +#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_FFMT 0x09000 /* Flexible Filter Mask Table - RW Array */ +#define E1000_FFVT 0x09800 /* Flexible Filter Value Table - RW Array */ + +/* 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_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_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_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_RXDCTL E1000_RXDCTL +#define E1000_82542_RADV E1000_RADV +#define E1000_82542_RSRPD E1000_RSRPD +#define E1000_82542_TXDMAC E1000_TXDMAC +#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 + +/* Statistics counters collected by the MAC */ +struct e1000_hw_stats { + uint64_t crcerrs; + uint64_t algnerrc; + uint64_t symerrs; + uint64_t rxerrc; + 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 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; +}; + +/* Structure containing variables used by the shared code (e1000_hw.c) */ +struct e1000_hw { + struct pci_device *pdev; + uint8_t *hw_addr; + e1000_mac_type mac_type; + e1000_phy_type phy_type; +#if 0 + uint32_t phy_init_script; +#endif + e1000_media_type media_type; + e1000_fc_type fc; +#if 0 + e1000_bus_speed bus_speed; + e1000_bus_width bus_width; + e1000_bus_type bus_type; +#endif + struct e1000_eeprom_info eeprom; +#if 0 + e1000_ms_type master_slave; + e1000_ms_type original_master_slave; + e1000_ffe_config ffe_config_state; +#endif + uint32_t io_base; + uint32_t phy_id; +#ifdef LINUX_DRIVER + uint32_t phy_revision; +#endif + uint32_t phy_addr; +#if 0 + uint32_t original_fc; +#endif + uint32_t txcw; + uint32_t autoneg_failed; +#if 0 + 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; + uint16_t phy_spd_default; +#endif + uint16_t autoneg_advertised; + uint16_t pci_cmd_word; +#if 0 + 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; +#endif + uint16_t device_id; + uint16_t vendor_id; +#if 0 + uint16_t subsystem_id; + uint16_t subsystem_vendor_id; +#endif + uint8_t revision_id; +#if 0 + uint8_t autoneg; + uint8_t mdix; + uint8_t forced_speed_duplex; + uint8_t wait_autoneg_complete; + uint8_t dma_fairness; +#endif + uint8_t mac_addr[NODE_ADDRESS_SIZE]; +#if 0 + uint8_t perm_mac_addr[NODE_ADDRESS_SIZE]; + boolean_t disable_polarity_correction; + boolean_t speed_downgraded; + e1000_dsp_config dsp_config_state; + boolean_t get_link_status; + boolean_t serdes_link_down; +#endif + boolean_t tbi_compatibility_en; + boolean_t tbi_compatibility_on; +#if 0 + boolean_t phy_reset_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; +#endif +}; + + +#define E1000_EEPROM_SWDPIN0 0x0001 /* SWDPIN 0 EEPROM Value */ +#define E1000_EEPROM_LED_LOGIC 0x0020 /* Led Logic Word */ + +/* 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_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_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 */ + +/* 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_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_ASDV 0x00000300 /* Auto speed detect value */ +#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 */ + +/* 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 + +/* 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_LINK_MODE_MASK 0x00C00000 +#define E1000_CTRL_EXT_LINK_MODE_GMII 0x00000000 +#define E1000_CTRL_EXT_LINK_MODE_TBI 0x00C00000 +#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 + +/* 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 + +/* LED Control */ +#define E1000_LEDCTL_LED0_MODE_MASK 0x0000000F +#define E1000_LEDCTL_LED0_MODE_SHIFT 0 +#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_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_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_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 + +/* 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 + +/* 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 + +/* 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 + +/* 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_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 */ + +/* 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 */ + +/* 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 0x000000FF /* TXDCTL Prefetch Threshold */ +#define E1000_TXDCTL_HTHRESH 0x0000FF00 /* TXDCTL Host Threshold */ +#define E1000_TXDCTL_WTHRESH 0x00FF0000 /* 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 */ + +/* 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 */ + +/* 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 */ + +/* 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_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_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_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 */ + +/* Wake Up Packet Length */ +#define E1000_WUPL_LENGTH_MASK 0x0FFF /* Only the lower 12 bits are valid */ + +#define E1000_MDALIGN 4096 + +/* 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 0x3 /* EEPROM read opcode */ +#define EEPROM_WRITE_OPCODE_SPI 0x2 /* EEPROM write opcode */ +#define EEPROM_A8_OPCODE_SPI 0x8 /* opcode bit-3 = address bit-8 */ +#define EEPROM_WREN_OPCODE_SPI 0x6 /* EEPROM set Write Enable latch */ +#define EEPROM_WRDI_OPCODE_SPI 0x4 /* EEPROM reset Write Enable latch */ +#define EEPROM_RDSR_OPCODE_SPI 0x5 /* EEPROM read Status register */ +#define EEPROM_WRSR_OPCODE_SPI 0x1 /* EEPROM write Status register */ + +/* EEPROM Size definitions */ +#define EEPROM_SIZE_16KB 0x1800 +#define EEPROM_SIZE_8KB 0x1400 +#define EEPROM_SIZE_4KB 0x1000 +#define EEPROM_SIZE_2KB 0x0C00 +#define EEPROM_SIZE_1KB 0x0800 +#define EEPROM_SIZE_512B 0x0400 +#define EEPROM_SIZE_128B 0x0000 +#define EEPROM_SIZE_MASK 0x1C00 + +/* EEPROM Word Offsets */ +#define EEPROM_COMPAT 0x0003 +#define EEPROM_ID_LED_SETTINGS 0x0004 +#define EEPROM_SERDES_AMPLITUDE 0x0006 /* For SERDES output amplitude adjustment. */ +#define EEPROM_INIT_CONTROL1_REG 0x000A +#define EEPROM_INIT_CONTROL2_REG 0x000F +#define EEPROM_INIT_CONTROL3_PORT_B 0x0014 +#define EEPROM_INIT_CONTROL3_PORT_A 0x0024 +#define EEPROM_CFG 0x0012 +#define EEPROM_FLASH_VERSION 0x0032 +#define EEPROM_CHECKSUM_REG 0x003F + +/* Word definitions for ID LED Settings */ +#define ID_LED_RESERVED_0000 0x0000 +#define ID_LED_RESERVED_FFFF 0xFFFF +#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_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 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 + +/* 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 16 +#define E1000_CT_SHIFT 4 +#define E1000_COLLISION_DISTANCE 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 E1000_TIPG_IPGR2_SHIFT 20 + +#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 + +/* PBA constants */ +#define E1000_PBA_16K 0x0010 /* 16KB, default TX allocation */ +#define E1000_PBA_22K 0x0016 +#define E1000_PBA_24K 0x0018 +#define E1000_PBA_30K 0x001E +#define E1000_PBA_40K 0x0028 +#define E1000_PBA_48K 0x0030 /* 48KB, default RX allocation */ + +/* 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 + +#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 */ + +/* 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 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 + +/* 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 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 + +#define MAX_PHY_REG_ADDRESS 0x1F /* 5 bit address bus (0-0x1F) */ +#define MAX_PHY_MULTI_PAGE_REG 0xF /*Registers that are equal on all pages*/ +/* 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 */ + +/* 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 0x0001 +#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_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 0x0010 +#define IGP01E1000_PLHR_AUTONEG_ACTIVE 0x0008 +#define IGP01E1000_PLHR_VALID_CHANNEL_D 0x0004 +#define IGP01E1000_PLHR_VALID_CHANNEL_C 0x0002 +#define IGP01E1000_PLHR_VALID_CHANNEL_B 0x0001 +#define IGP01E1000_PLHR_VALID_CHANNEL_A 0x0000 + +/* 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 + +/* IGP01E1000 DSP reset macros */ +#define DSP_RESET_ENABLE 0x0 +#define DSP_RESET_DISABLE 0x2 +#define E1000_MAX_DSP_RESETS 10 + +/* IGP01E1000 AGC Registers */ + +#define IGP01E1000_AGC_LENGTH_SHIFT 7 /* Coarse - 13:11, Fine - 10:7 */ + +/* 7 bits (3 Coarse + 4 Fine) --> 128 optional values */ +#define IGP01E1000_AGC_LENGTH_TABLE_SIZE 128 + +/* The precision of the length is +/- 10 meters */ +#define IGP01E1000_AGC_RANGE 10 + +/* 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 + +/* Bit definitions for valid PHY IDs. */ +#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 + +/* 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_ */ diff --git a/src/drivers/net/eepro.c b/src/drivers/net/eepro.c new file mode 100644 index 00000000..b27c9f7f --- /dev/null +++ b/src/drivers/net/eepro.c @@ -0,0 +1,625 @@ +#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. +***************************************************************************/ + +/* + * 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" +#include "isa.h" +/* we use timer2 for microsecond waits */ +#include "timer.h" + +#undef DEBUG /* only after include files */ + +/* 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) + +/* 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 */ +#define eepro_full_reset(ioaddr) outb(RESET_CMD, ioaddr); udelay(40); + +/* do a nice reset */ +#define eepro_sel_reset(ioaddr) { \ + outb(SEL_RESET_CMD, ioaddr); \ + SLOW_DOWN; \ + SLOW_DOWN; \ + } + +/* 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 short ioaddr = 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(ioaddr); /* be careful, bank2 now */ + temp_reg = inb(ioaddr + eeprom_reg); +#ifdef DEBUG + printf("Stepping %d\n", temp_reg >> 5); +#endif + if (temp_reg & 0x10) /* check the TurnOff Enable bit */ + outb(temp_reg & 0xEF, ioaddr + eeprom_reg); + for (i = 0; i < ETH_ALEN; i++) /* fill the MAC address */ + outb(nic->node_addr[i], ioaddr + I_ADD_REG0 + i); + temp_reg = inb(ioaddr + REG1); + /* setup Transmit Chaining and discard bad RCV frames */ + outb(temp_reg | XMT_Chain_Int | XMT_Chain_ErrStop + | RCV_Discard_BadFrame, ioaddr + REG1); + temp_reg = inb(ioaddr + REG2); /* match broadcast */ + outb(temp_reg | 0x14, ioaddr + REG2); + temp_reg = inb(ioaddr + REG3); + outb(temp_reg & 0x3F, ioaddr + REG3); /* clear test mode */ + /* set the receiving mode */ + eepro_sw2bank1(ioaddr); /* be careful, bank1 now */ + /* initialise the RCV and XMT upper and lower limits */ + outb(RCV_LOWER_LIMIT, ioaddr + RCV_LOWER_LIMIT_REG); + outb(RCV_UPPER_LIMIT, ioaddr + RCV_UPPER_LIMIT_REG); + outb(XMT_LOWER_LIMIT, ioaddr + xmt_lower_limit_reg); + outb(XMT_UPPER_LIMIT, ioaddr + xmt_upper_limit_reg); + eepro_sw2bank0(ioaddr); /* Switch back to bank 0 */ + eepro_clear_int(ioaddr); + /* Initialise RCV */ + rx_start = (unsigned int)bus_to_virt(RCV_LOWER_LIMIT << 8); + outw(RCV_LOWER_LIMIT << 8, ioaddr + RCV_BAR); + outw(((RCV_UPPER_LIMIT << 8) | 0xFE), ioaddr + RCV_STOP); + /* Intialise XMT */ + outw((XMT_LOWER_LIMIT << 8), ioaddr + xmt_bar); + eepro_sel_reset(ioaddr); + tx_start = tx_end = (unsigned int)bus_to_virt(XMT_LOWER_LIMIT << 8); + tx_last = 0; + eepro_en_rx(ioaddr); +} + +/************************************************************************** +POLL - Wait for a frame +***************************************************************************/ +static int eepro_poll(struct nic *nic, int retrieve) +{ + unsigned int rcv_car = virt_to_bus((void *)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(ioaddr + STATUS_REG) & 0x40) == 0) + return (0); + outb(0x40, ioaddr + STATUS_REG); +#endif + outw(rcv_car, ioaddr + HOST_ADDRESS_REG); + rcv_event = inw(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(ioaddr + IO_PORT); + rcv_next_frame = inw(ioaddr + IO_PORT); + rcv_size = inw(ioaddr + IO_PORT); +#if 0 + printf("%hX %hX %d %hhX\n", rcv_status, rcv_next_frame, rcv_size, + inb(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(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 = virt_to_bus((void *) (rx_start + RCV_HEADER + rcv_size)); + rx_start = (unsigned int)bus_to_virt(rcv_next_frame << 8); + if (rcv_car == 0) + rcv_car = ((RCV_UPPER_LIMIT << 8) | 0xff); + outw(rcv_car - 1, 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, ioaddr + HOST_ADDRESS_REG); + outw(XMT_CMD, ioaddr + IO_PORT); + outw(0, ioaddr + IO_PORT); + outw(end, ioaddr + IO_PORT); + outw(length, ioaddr + IO_PORT); + outsw(ioaddr + IO_PORT, d, ETH_ALEN / 2); + outsw(ioaddr + IO_PORT, nic->node_addr, ETH_ALEN / 2); + type = htons(t); + outsw(ioaddr + IO_PORT, &type, sizeof(type) / 2); + outsw(ioaddr + IO_PORT, p, (s + 3) >> 1); + /* A dummy read to flush the DRAM write pipeline */ + status = inw(ioaddr + IO_PORT); + outw(last, ioaddr + xmt_bar); + outb(XMT_CMD, 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(ioaddr + IO_PORT)) & TX_DONE_BIT) == 0) { + udelay(40); + boguscount--; + continue; + } +#if DEBUG + if ((status & 0x2000) == 0) + printf("Transmit status %hX\n", status); +#endif + } +} + +/************************************************************************** +DISABLE - Turn off ethernet interface +***************************************************************************/ +static void eepro_disable(struct dev *dev __unused) +{ + eepro_sw2bank0(ioaddr); /* Switch to bank 0 */ + /* Flush the Tx and disable Rx */ + outb(STOP_RCV_CMD, ioaddr); + tx_start = tx_end = (unsigned int) (bus_to_virt(XMT_LOWER_LIMIT << 8)); + tx_last = 0; + /* Reset the 82595 */ + eepro_full_reset(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(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(struct nic *nic) +{ + int i, id, counter, l_eepro = 0; + union { + unsigned char caddr[ETH_ALEN]; + unsigned short saddr[ETH_ALEN/2]; + } station_addr; + char *name; + + 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 */ + station_addr.saddr[2] = read_eeprom(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(2); + } + station_addr.saddr[1] = read_eeprom(3); + station_addr.saddr[0] = read_eeprom(4); + if (l_eepro) + name = "Intel EtherExpress 10 ISA"; + else if (read_eeprom(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]; + } + printf("\n%s ioaddr %#hX, addr %!", name, ioaddr, 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(5), ee_BNC_TPE) ? "BNC" : "TP"); + return (1); +} + +/************************************************************************** +PROBE - Look for an adapter, this routine's visible to the outside +***************************************************************************/ +static int eepro_probe(struct dev *dev, unsigned short *probe_addrs) +{ + struct nic *nic = (struct nic *)dev; + unsigned short *p; + /* same probe list as the Linux driver */ + static unsigned short ioaddrs[] = { + 0x300, 0x210, 0x240, 0x280, 0x2C0, 0x200, 0x320, 0x340, 0x360, 0}; + + if (probe_addrs == 0 || probe_addrs[0] == 0) + probe_addrs = ioaddrs; + for (p = probe_addrs; (ioaddr = *p) != 0; p++) { + if (eepro_probe1(nic)) + break; + } + if (*p == 0) + return (0); + + nic->irqno = 0; + nic->ioaddr = *p; + + eepro_reset(nic); + /* point to NIC specific routines */ + dev->disable = eepro_disable; + nic->poll = eepro_poll; + nic->transmit = eepro_transmit; + nic->irq = eepro_irq; + /* Based on PnP ISA map */ + dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR); + dev->devid.device_id = htons(0x828a); + return 1; +} + +static struct isa_driver eepro_driver __isa_driver = { + .type = NIC_DRIVER, + .name = "EEPRO", + .probe = eepro_probe, + .ioaddrs = 0, +}; diff --git a/src/drivers/net/eepro100.c b/src/drivers/net/eepro100.c new file mode 100644 index 00000000..e10bc84b --- /dev/null +++ b/src/drivers/net/eepro100.c @@ -0,0 +1,841 @@ +/* + * 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 "pci.h" +#include "timer.h" + +static int ioaddr; + +typedef unsigned char u8; +typedef signed char s8; +typedef unsigned short u16; +typedef signed short s16; +typedef unsigned int u32; +typedef signed int s32; + +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]; +}; + +#define RXFD_COUNT 4 +static struct RxFD rxfds[RXFD_COUNT]; +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 + 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; + + 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); + load_timer2(10*TICKS_PER_MS); /* timeout 10 ms for transmit */ + while (!txfd.status && timer2_running()) + /* 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 dev *dev __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 dev *dev, struct pci_device *p) +{ + struct nic *nic = (struct nic *)dev; + unsigned short sum = 0; + int i; + int read_cmd, ee_size; + int options; + int rx_mode; + + /* we cache only the first few words of the EEPROM data + be careful not to access beyond this array */ + unsigned short eeprom[16]; + + if (p->ioaddr == 0) + return 0; + ioaddr = p->ioaddr & ~3; /* Mask the bit that says "this is an io addr" */ + nic->ioaddr = ioaddr; + + adjust_pci_device(p); + + /* Copy IRQ from PCI information */ + nic->irqno = p->irq; + + 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;inode_addr[i] = (eeprom[i/2] >> (8*(i&1))) & 0xff; + } + printf ("Ethernet addr: %!\n", 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;inode_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)."); + + load_timer2(10*TICKS_PER_MS); + while (!txfd.status && timer2_running()) + /* 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(dev); + return 0; + } + + dev->disable = eepro100_disable; + nic->poll = eepro100_poll; + nic->transmit = eepro100_transmit; + nic->irq = eepro100_irq; + 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 pci_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, 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 */ + + +static struct pci_driver eepro100_driver __pci_driver = { + .type = NIC_DRIVER, + .name = "EEPRO100", + .probe = eepro100_probe, + .ids = eepro100_nics, + .id_count = sizeof(eepro100_nics)/sizeof(eepro100_nics[0]), + .class = 0 +}; diff --git a/src/drivers/net/epic100.c b/src/drivers/net/epic100.c new file mode 100644 index 00000000..cd83edf4 --- /dev/null +++ b/src/drivers/net/epic100.c @@ -0,0 +1,520 @@ + +/* 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 "pci.h" +#include "nic.h" +#include "timer.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 dev *dev); +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 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. */ +static struct epic_rx_desc rx_ring[RX_RING_SIZE] + __attribute__ ((aligned(4))); +static struct epic_tx_desc tx_ring[TX_RING_SIZE] + __attribute__ ((aligned(4))); +static unsigned char rx_packet[PKT_BUF_SZ * RX_RING_SIZE]; +static unsigned char tx_packet[PKT_BUF_SZ * TX_RING_SIZE]; + +/***********************************************************************/ +/* Externally visible functions */ +/***********************************************************************/ + + + static int +epic100_probe(struct dev *dev, struct pci_device *pci) +{ + struct nic *nic = (struct nic *)dev; + 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); + + printf(" I/O %#hX %! ", ioaddr, 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(); + + dev->disable = epic100_disable; + nic->poll = epic100_poll; + nic->transmit = epic100_transmit; + nic->irq = epic100_irq; + + 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; + + /* 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=%hX. 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); + + load_timer2(10*TICKS_PER_MS); /* timeout 10 ms for transmit */ + while ((le32_to_cpu(tx_ring[entry].status) & (TRING_OWN)) && timer2_running()) + /* Wait */; + + if ((le32_to_cpu(tx_ring[entry].status) & TRING_OWN) != 0) + printf("Oops, transmitter timeout, status=%hX\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 dev *dev __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 pci_id epic100_nics[] = { +PCI_ROM(0x10b8, 0x0005, "epic100", "SMC EtherPowerII"), /* SMC 83c170 EPIC/100 */ +PCI_ROM(0x10b8, 0x0006, "smc-83c175", "SMC EPIC/C 83c175"), +}; + +static struct pci_driver epic100_driver __pci_driver = { + .type = NIC_DRIVER, + .name = "EPIC100", + .probe = epic100_probe, + .ids = epic100_nics, + .id_count = sizeof(epic100_nics)/sizeof(epic100_nics[0]), + .class = 0, +}; diff --git a/src/drivers/net/epic100.h b/src/drivers/net/epic100.h new file mode 100644 index 00000000..61bd1d94 --- /dev/null +++ b/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/src/drivers/net/forcedeth.c b/src/drivers/net/forcedeth.c new file mode 100644 index 00000000..326c4cca --- /dev/null +++ b/src/drivers/net/forcedeth.c @@ -0,0 +1,1039 @@ +/************************************************************************** +* 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 +* +* 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.22, 19 Jan 2004 +* +* +* REVISION HISTORY: +* ================ +* v1.0 01-31-2004 timlegge Initial port of Linux driver +* v1.1 02-03-2004 timlegge Large Clean up, first release +* +* 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 "pci.h" +/* Include timer support functions */ +#include "timer.h" + +#define drv_version "v1.1" +#define drv_date "02-03-2004" + +//#define TFTM_DEBUG +#ifdef TFTM_DEBUG +#define dprintf(x) printf x +#else +#define dprintf(x) +#endif + +typedef unsigned char u8; +typedef signed char s8; +typedef unsigned short u16; +typedef signed short s16; +typedef unsigned int u32; +typedef signed int s32; + +/* 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)) + +unsigned long BASE; +/* NIC specific static variables go here */ + + +/* + * Hardware access: + */ + +#define DEV_NEED_LASTPACKET1 0x0001 +#define DEV_IRQMASK_1 0x0002 +#define DEV_IRQMASK_2 0x0004 +#define DEV_NEED_TIMERIRQ 0x0008 + +enum { + NvRegIrqStatus = 0x000, +#define NVREG_IRQSTAT_MIIEVENT 0040 +#define NVREG_IRQSTAT_MASK 0x1ff + NvRegIrqMask = 0x004, +#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|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 0x5ee + 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 + + 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, + + 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 10 +#define NVREG_LINKSPEED_100 100 +#define NVREG_LINKSPEED_1000 1000 + NvRegUnknownSetupReg5 = 0x130, +#define NVREG_UNKSETUP5_BIT31 (1<<31) + NvRegUnknownSetupReg3 = 0x134, +#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 + 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 0x4000 +#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 0x10000 +#define NVREG_MIICTL_WRITE 0x08000 +#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 + + 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 NV_TX_LASTPACKET (1<<0) +#define NV_TX_RETRYERROR (1<<3) +#define NV_TX_LASTPACKET1 (1<<8) +#define NV_TX_DEFERRED (1<<10) +#define NV_TX_CARRIERLOST (1<<11) +#define NV_TX_LATECOLLISION (1<<12) +#define NV_TX_UNDERFLOW (1<<13) +#define NV_TX_ERROR (1<<14) +#define NV_TX_VALID (1<<15) + +#define NV_RX_DESCRIPTORVALID (1<<0) +#define NV_RX_MISSEDFRAME (1<<1) +#define NV_RX_SUBSTRACT1 (1<<3) +#define NV_RX_ERROR1 (1<<7) +#define NV_RX_ERROR2 (1<<8) +#define NV_RX_ERROR3 (1<<9) +#define NV_RX_ERROR4 (1<<10) +#define NV_RX_CRCERR (1<<11) +#define NV_RX_OVERFLOW (1<<12) +#define NV_RX_FRAMINGERR (1<<13) +#define NV_RX_ERROR (1<<14) +#define NV_RX_AVAIL (1<<15) + +/* 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 (2*HZ) +#define DEFAULT_MTU 1500 /* also maximum supported, at least for now */ + +#define RX_RING 4 +#define TX_RING 2 +/* limited to 1 packet until we understand NV_TX_LASTPACKET */ +#define TX_LIMIT_STOP 10 +#define TX_LIMIT_START 5 + +/* rx/tx mac addr + type + vlan + align + slack*/ +#define RX_NIC_BUFSIZE (DEFAULT_MTU + 64) +/* even more slack */ +#define RX_ALLOC_BUFSIZE (DEFAULT_MTU + 128) + +#define OOM_REFILL (1+HZ/20) +#define POLL_WAIT (1+HZ/100) + +struct ring_desc { + u32 PacketBuffer; + u16 Length; + u16 Flags; +}; + + +/* Define the TX Descriptor */ +static struct ring_desc tx_ring[TX_RING]; + +/* Create a static buffer of size RX_BUF_SZ for each +TX Descriptor. All descriptors point to a +part of this buffer */ +static unsigned char txb[TX_RING * RX_NIC_BUFSIZE]; + +/* Define the TX Descriptor */ +static struct ring_desc rx_ring[RX_RING]; + +/* Create a static buffer of size RX_BUF_SZ for each +RX Descriptor All descriptors point to a +part of this buffer */ +static unsigned char rxb[RX_RING * RX_NIC_BUFSIZE]; + +/* Private Storage for the NIC */ +struct forcedeth_private { + /* General data: + * Locking: spin_lock(&np->lock); */ + int in_shutdown; + u32 linkspeed; + int duplex; + int phyaddr; + + /* General data: RO fields */ + u8 *ring_addr; + u32 orig_mac[2]; + u32 irqmask; + /* rx specific fields. + * Locking: Within irq hander or disable_irq+spin_lock(&np->lock); + */ + struct ring_desc *rx_ring; + unsigned int cur_rx, refill_rx; + struct sk_buff *rx_skbuff[RX_RING]; + u32 rx_dma[RX_RING]; + unsigned int rx_buf_sz; + + /* + * tx specific fields. + */ + struct ring_desc *tx_ring; + unsigned int next_tx, nic_tx; + struct sk_buff *tx_skbuff[TX_RING]; + u32 tx_dma[TX_RING]; + u16 tx_flags; +} npx; + +static struct forcedeth_private *np; + +static inline void pci_push(u8 * base) +{ + /* force out pending posted writes */ + readl(base); +} +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; + int was_running; + u32 reg; + int retval; + + writel(NVREG_MIISTAT_MASK, base + NvRegMIIStatus); + was_running = 0; + reg = readl(base + NvRegAdapterControl); + if (reg & NVREG_ADAPTCTL_RUNNING) { + was_running = 1; + writel(reg & ~NVREG_ADAPTCTL_RUNNING, + base + NvRegAdapterControl); + } + reg = readl(base + NvRegMIIControl); + if (reg & NVREG_MIICTL_INUSE) { + writel(NVREG_MIICTL_INUSE, base + NvRegMIIControl); + udelay(NV_MIIBUSY_DELAY); + } + + reg = + NVREG_MIICTL_INUSE | (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 { + /* FIXME: why is that required? */ + udelay(50); + retval = readl(base + NvRegMIIData); + dprintf(("mii_rw read from reg %d at PHY %d: 0x%x.\n", + miireg, addr, retval)); + } + if (was_running) { + reg = readl(base + NvRegAdapterControl); + writel(reg | NVREG_ADAPTCTL_RUNNING, + base + NvRegAdapterControl); + } + return retval; +} + +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, + base + NvRegTxRxControl); + pci_push(base); + udelay(NV_TXRX_RESET_DELAY); + writel(NVREG_TXRXCTL_BIT2, 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]); + rx_ring[i].Length = cpu_to_le16(RX_NIC_BUFSIZE); + wmb(); + rx_ring[i].Flags = cpu_to_le16(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, newdup; + u32 newls; + 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; + } + if (np->duplex != newdup || np->linkspeed != newls) { + np->duplex = newdup; + np->linkspeed = newls; + return 1; + } + return 0; +} + + + +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].Flags = 0; + } + + np->cur_rx = 0; + np->refill_rx = 0; + for (i = 0; i < RX_RING; i++) { + rx_ring[i].Flags = 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 + NvRegAdapterControl); + writel(0, base + NvRegLinkSpeed); + writel(0, base + NvRegUnknownTransmitterReg); + txrx_reset(nic); + writel(0, base + NvRegUnknownSetupReg6); + + /* 2) initialize descriptor rings */ + np->in_shutdown = 0; + oom = init_ring(nic); + + /* 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) continue setup */ + np->linkspeed = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10; + np->duplex = 0; + writel(NVREG_UNKSETUP3_VAL1, base + NvRegUnknownSetupReg3); + writel(0, base + NvRegTxRxControl); + pci_push(base); + writel(NVREG_TXRXCTL_BIT1, 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); + + /* 5) Find a suitable PHY */ + writel(NVREG_MIISPEED_BIT8 | NVREG_MIIDELAY, base + NvRegMIISpeed); + for (i = 1; i < 32; i++) { + int id1, id2; + + id1 = mii_rw(nic, i, MII_PHYSID1, MII_READ); + if (id1 < 0) + continue; + id2 = mii_rw(nic, i, MII_PHYSID2, MII_READ); + if (id2 < 0) + continue; + dprintf(("open: Found PHY %04x:%04x at address %d.\n", + id1, id2, i)); + np->phyaddr = i; + + update_linkspeed(nic); + + break; + } + if (i == 32) { + printf("open: failing due to lack of suitable PHY.\n"); + ret = -1; + goto out_drain; + } + + printf("%d-Mbs Link, %s-Duplex\n", + np->linkspeed & NVREG_LINKSPEED_10 ? 10 : 100, + np->duplex ? "Full" : "Half"); + /* 6) continue setup */ + writel(NVREG_MISC1_FORCE | (np->duplex ? 0 : 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); + + /* FIXME: I cheated and used the calculator to get a random number */ + i = 75963081; + 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, base + NvRegAdapterControl); + writel(NVREG_UNKSETUP4_VAL, base + NvRegUnknownSetupReg4); + writel(NVREG_WAKEUPFLAGS_VAL, base + NvRegWakeUpFlags); + + /* 7) start packet processing */ + 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); + + 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(NVREG_ADAPTCTL_RUNNING, base + NvRegAdapterControl); + + writel(0, base + NvRegIrqMask); + pci_push(base); + writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus); + 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); + //start_rx(nic); + start_tx(nic); + + if (! + (mii_rw(nic, np->phyaddr, MII_BMSR, MII_READ) & + BMSR_ANEGCOMPLETE)) { + printf("no link during initialization.\n"); + } + + udelay(10000); + out_drain: + 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 */ + + struct ring_desc *prd; + int len; + int i; + + i = np->cur_rx % RX_RING; + prd = &rx_ring[i]; + + if ( ! (prd->Flags & cpu_to_le16(NV_RX_DESCRIPTORVALID)) ) { + return 0; + } + + if ( ! retrieve ) return 1; + + /* got a valid packet - forward it to the network core */ + len = cpu_to_le16(prd->Length); + nic->packetlen = len; + //hex_dump(rxb + (i * RX_NIC_BUFSIZE), len); + memcpy(nic->packet, rxb + + (i * RX_NIC_BUFSIZE), nic->packetlen); + + 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; + //u16 status; + 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); + tx_ring[nr].Length = cpu_to_le16(s - 1); + + wmb(); + tx_ring[nr].Flags = np->tx_flags; + + writel(NVREG_TXRXCTL_KICK, base + NvRegTxRxControl); + pci_push(base); + tx_ring[nr].Flags = np->tx_flags; + np->next_tx++; +} + +/************************************************************************** +DISABLE - Turn off ethernet interface +***************************************************************************/ +static void forcedeth_disable(struct dev *dev __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; + } +} + +/************************************************************************** +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 dev *dev, struct pci_device *pci) +{ + struct nic *nic = (struct nic *) dev; + unsigned long addr; + int sz; + u8 *base; + + if (pci->ioaddr == 0) + return 0; + + printf("forcedeth.c: Found %s, vendor=0x%hX, device=0x%hX\n", + pci->name, pci->vendor, pci->dev_id); + + nic->irqno = 0; + nic->ioaddr = pci->ioaddr & ~3; + + /* 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; + //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 + printf("%s: MAC Address %!, ", pci->name, nic->node_addr); + + np->tx_flags = + cpu_to_le16(NV_TX_LASTPACKET | NV_TX_LASTPACKET1 | + NV_TX_VALID); + switch (pci->dev_id) { + case 0x01C3: // nforce + np->irqmask = NVREG_IRQMASK_WANTED_2; + np->irqmask |= NVREG_IRQ_TIMER; + break; + case 0x0066: // nforce2 + np->tx_flags |= cpu_to_le16(NV_TX_LASTPACKET1); + np->irqmask = NVREG_IRQMASK_WANTED_2; + np->irqmask |= NVREG_IRQ_TIMER; + break; + case 0x00D6: // nforce3 + np->tx_flags |= cpu_to_le16(NV_TX_LASTPACKET1); + np->irqmask = NVREG_IRQMASK_WANTED_2; + np->irqmask |= NVREG_IRQ_TIMER; + + } + dprintf(("%s: forcedeth.c: subsystem: %hX:%hX bound to %s\n", + pci->name, pci->vendor, pci->dev_id, pci->name)); + + forcedeth_reset(nic); +// if (board_found && valid_link) + /* point to NIC specific routines */ + dev->disable = forcedeth_disable; + nic->poll = forcedeth_poll; + nic->transmit = forcedeth_transmit; + nic->irq = forcedeth_irq; + return 1; +// } + /* else */ +} + +static struct pci_id forcedeth_nics[] = { + PCI_ROM(0x10de, 0x01C3, "nforce", "nForce Ethernet Controller"), + PCI_ROM(0x10de, 0x0066, "nforce2", "nForce2 Ethernet Controller"), + PCI_ROM(0x10de, 0x00D6, "nforce3", "nForce3 Ethernet Controller"), +}; + +static struct pci_driver forcedeth_driver __pci_driver = { + .type = NIC_DRIVER, + .name = "forcedeth", + .probe = forcedeth_probe, + .ids = forcedeth_nics, + .id_count = sizeof(forcedeth_nics) / sizeof(forcedeth_nics[0]), + .class = 0, +}; diff --git a/src/drivers/net/hfa384x.h b/src/drivers/net/hfa384x.h new file mode 100644 index 00000000..9fcbd294 --- /dev/null +++ b/src/drivers/net/hfa384x.h @@ -0,0 +1,2744 @@ +/* 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_BSS ((UINT16)1) +#define HFA384x_PORTTYPE_WDS ((UINT16)2) +#define HFA384x_PORTTYPE_IBSS ((UINT16)3) +#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_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_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 + +/*--- 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_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 + +/*-------------------------------------------------------------------- +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) + +/*-------------------------------------------------------------------- +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_CNFHOSTAUTH ((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_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_WEPKEYDISABLE ((UINT16)0xFCB6) // NEW +#define HFA384x_RID_WEPKEYMAPINDEX ((UINT16)0xFCB7) // NEW AP +#define HFA384x_RID_BROADCASTKEYID ((UINT16)0xFCB8) // NEW AP +#define HFA384x_RID_ENTSECFLAGEYID ((UINT16)0xFCB9) // NEW AP +#define HFA384x_RID_CNFPASSIVESCANCTRL ((UINT16)0xFCB9) // NEW STA +#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_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_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_UNKNOWN407 ((UINT16)0x0407) +#define HFA384x_PDR_UNKNOWN408 ((UINT16)0x0408) +#define HFA384x_PDR_UNKNOWN409 ((UINT16)0x0409) +#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 + + +/*--- 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_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 */ +#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))) + +/* Host Maintained State Info */ +#define HFA384x_STATE_PREINIT 0 +#define HFA384x_STATE_INIT 1 +#define HFA384x_STATE_RUNNING 2 + +/*=============================================================*/ +/*------ Types and their related constants --------------------*/ + +/*-------------------------------------------------------------*/ +/* Commonly used basic types */ +typedef struct hfa384x_bytestr +{ + UINT16 len __WLAN_ATTRIB_PACK__; + UINT8 data[0] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_bytestr_t; + +typedef struct hfa384x_bytestr32 +{ + UINT16 len __WLAN_ATTRIB_PACK__; + UINT8 data[32] __WLAN_ATTRIB_PACK__; +} __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 __WLAN_ATTRIB_PACK__; + UINT16 rid __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_rec_t; + +typedef struct hfa384x_record16 +{ + UINT16 reclen __WLAN_ATTRIB_PACK__; + UINT16 rid __WLAN_ATTRIB_PACK__; + UINT16 val __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_rec16_t; + +typedef struct hfa384x_record32 +{ + UINT16 reclen __WLAN_ATTRIB_PACK__; + UINT16 rid __WLAN_ATTRIB_PACK__; + UINT32 val __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_rec32; + +/*-- Hardware/Firmware Component Information ----------*/ +typedef struct hfa384x_compident +{ + UINT16 id __WLAN_ATTRIB_PACK__; + UINT16 variant __WLAN_ATTRIB_PACK__; + UINT16 major __WLAN_ATTRIB_PACK__; + UINT16 minor __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_compident_t; + +typedef struct hfa384x_caplevel +{ + UINT16 role __WLAN_ATTRIB_PACK__; + UINT16 id __WLAN_ATTRIB_PACK__; + UINT16 variant __WLAN_ATTRIB_PACK__; + UINT16 bottom __WLAN_ATTRIB_PACK__; + UINT16 top __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_caplevel_t; + +/*-- Configuration Record: cnfPortType --*/ +typedef struct hfa384x_cnfPortType +{ + UINT16 cnfPortType __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfPortType_t; + +/*-- Configuration Record: cnfOwnMACAddress --*/ +typedef struct hfa384x_cnfOwnMACAddress +{ + UINT8 cnfOwnMACAddress[6] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfOwnMACAddress_t; + +/*-- Configuration Record: cnfDesiredSSID --*/ +typedef struct hfa384x_cnfDesiredSSID +{ + UINT8 cnfDesiredSSID[34] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfDesiredSSID_t; + +/*-- Configuration Record: cnfOwnChannel --*/ +typedef struct hfa384x_cnfOwnChannel +{ + UINT16 cnfOwnChannel __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfOwnChannel_t; + +/*-- Configuration Record: cnfOwnSSID --*/ +typedef struct hfa384x_cnfOwnSSID +{ + UINT8 cnfOwnSSID[34] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfOwnSSID_t; + +/*-- Configuration Record: cnfOwnATIMWindow --*/ +typedef struct hfa384x_cnfOwnATIMWindow +{ + UINT16 cnfOwnATIMWindow __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfOwnATIMWindow_t; + +/*-- Configuration Record: cnfSystemScale --*/ +typedef struct hfa384x_cnfSystemScale +{ + UINT16 cnfSystemScale __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfSystemScale_t; + +/*-- Configuration Record: cnfMaxDataLength --*/ +typedef struct hfa384x_cnfMaxDataLength +{ + UINT16 cnfMaxDataLength __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfMaxDataLength_t; + +/*-- Configuration Record: cnfWDSAddress --*/ +typedef struct hfa384x_cnfWDSAddress +{ + UINT8 cnfWDSAddress[6] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfWDSAddress_t; + +/*-- Configuration Record: cnfPMEnabled --*/ +typedef struct hfa384x_cnfPMEnabled +{ + UINT16 cnfPMEnabled __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfPMEnabled_t; + +/*-- Configuration Record: cnfPMEPS --*/ +typedef struct hfa384x_cnfPMEPS +{ + UINT16 cnfPMEPS __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfPMEPS_t; + +/*-- Configuration Record: cnfMulticastReceive --*/ +typedef struct hfa384x_cnfMulticastReceive +{ + UINT16 cnfMulticastReceive __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfMulticastReceive_t; + +/*-- Configuration Record: cnfAuthentication --*/ +#define HFA384x_CNFAUTHENTICATION_OPENSYSTEM 0x0001 +#define HFA384x_CNFAUTHENTICATION_SHAREDKEY 0x0002 + +/*-- Configuration Record: cnfMaxSleepDuration --*/ +typedef struct hfa384x_cnfMaxSleepDuration +{ + UINT16 cnfMaxSleepDuration __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfMaxSleepDuration_t; + +/*-- Configuration Record: cnfPMHoldoverDuration --*/ +typedef struct hfa384x_cnfPMHoldoverDuration +{ + UINT16 cnfPMHoldoverDuration __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfPMHoldoverDuration_t; + +/*-- Configuration Record: cnfOwnName --*/ +typedef struct hfa384x_cnfOwnName +{ + UINT8 cnfOwnName[34] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfOwnName_t; + +/*-- Configuration Record: cnfOwnDTIMPeriod --*/ +typedef struct hfa384x_cnfOwnDTIMPeriod +{ + UINT16 cnfOwnDTIMPeriod __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfOwnDTIMPeriod_t; + +/*-- Configuration Record: cnfWDSAddress --*/ +typedef struct hfa384x_cnfWDSAddressN +{ + UINT8 cnfWDSAddress[6] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfWDSAddressN_t; + +/*-- Configuration Record: cnfMulticastPMBuffering --*/ +typedef struct hfa384x_cnfMulticastPMBuffering +{ + UINT16 cnfMulticastPMBuffering __WLAN_ATTRIB_PACK__; +} __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__; +} __WLAN_ATTRIB_PACK__ hfa384x_GroupAddresses_t; + +/*-- Configuration Record: CreateIBSS --*/ +typedef struct hfa384x_CreateIBSS +{ + UINT16 CreateIBSS __WLAN_ATTRIB_PACK__; +} __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__; +} __WLAN_ATTRIB_PACK__ hfa384x_FragmentationThreshold_t; + +/*-- Configuration Record: RTSThreshold --*/ +typedef struct hfa384x_RTSThreshold +{ + UINT16 RTSThreshold __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_RTSThreshold_t; + +/*-- Configuration Record: TxRateControl --*/ +typedef struct hfa384x_TxRateControl +{ + UINT16 TxRateControl __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_TxRateControl_t; + +/*-- Configuration Record: PromiscuousMode --*/ +typedef struct hfa384x_PromiscuousMode +{ + UINT16 PromiscuousMode __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_PromiscuousMode_t; + +/*-- Configuration Record: ScanRequest (data portion only) --*/ +typedef struct hfa384x_ScanRequest_data +{ + UINT16 channelList __WLAN_ATTRIB_PACK__; + UINT16 txRate __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_ScanRequest_data_t; + +/*-- Configuration Record: HostScanRequest (data portion only) --*/ +typedef struct hfa384x_HostScanRequest_data +{ + UINT16 channelList __WLAN_ATTRIB_PACK__; + UINT16 txRate __WLAN_ATTRIB_PACK__; + hfa384x_bytestr32_t ssid __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_HostScanRequest_data_t; + +/*-- Configuration Record: JoinRequest (data portion only) --*/ +typedef struct hfa384x_JoinRequest_data +{ + UINT8 bssid[WLAN_BSSID_LEN] __WLAN_ATTRIB_PACK__; + UINT16 channel __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_JoinRequest_data_t; + +/*-- Configuration Record: authenticateStation (data portion only) --*/ +typedef struct hfa384x_authenticateStation_data +{ + UINT8 address[WLAN_ADDR_LEN] __WLAN_ATTRIB_PACK__; + UINT16 status __WLAN_ATTRIB_PACK__; + UINT16 algorithm __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_authenticateStation_data_t; + +/*-- Configuration Record: ChannelInfoRequest (data portion only) --*/ +typedef struct hfa384x_ChannelInfoRequest_data +{ + UINT16 channelList __WLAN_ATTRIB_PACK__; + UINT16 channelDwellTime __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_ChannelInfoRequest_data_t; + +/*-------------------------------------------------------------------- +Configuration Record Structures: Behavior Parameters +--------------------------------------------------------------------*/ + +/*-- Configuration Record: TickTime --*/ +typedef struct hfa384x_TickTime +{ + UINT16 TickTime __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_TickTime_t; + +/*-------------------------------------------------------------------- +Information Record Structures: NIC Information +--------------------------------------------------------------------*/ + +/*-- Information Record: MaxLoadTime --*/ +typedef struct hfa384x_MaxLoadTime +{ + UINT16 MaxLoadTime __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_MaxLoadTime_t; + +/*-- Information Record: DownLoadBuffer --*/ +/* NOTE: The page and offset are in AUX format */ +typedef struct hfa384x_downloadbuffer +{ + UINT16 page __WLAN_ATTRIB_PACK__; + UINT16 offset __WLAN_ATTRIB_PACK__; + UINT16 len __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_downloadbuffer_t; + +/*-- Information Record: PRIIdentity --*/ +typedef struct hfa384x_PRIIdentity +{ + UINT16 PRICompID __WLAN_ATTRIB_PACK__; + UINT16 PRIVariant __WLAN_ATTRIB_PACK__; + UINT16 PRIMajorVersion __WLAN_ATTRIB_PACK__; + UINT16 PRIMinorVersion __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_PRIIdentity_t; + +/*-- Information Record: PRISupRange --*/ +typedef struct hfa384x_PRISupRange +{ + UINT16 PRIRole __WLAN_ATTRIB_PACK__; + UINT16 PRIID __WLAN_ATTRIB_PACK__; + UINT16 PRIVariant __WLAN_ATTRIB_PACK__; + UINT16 PRIBottom __WLAN_ATTRIB_PACK__; + UINT16 PRITop __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_PRISupRange_t; + +/*-- Information Record: CFIActRanges --*/ +typedef struct hfa384x_CFIActRanges +{ + UINT16 CFIRole __WLAN_ATTRIB_PACK__; + UINT16 CFIID __WLAN_ATTRIB_PACK__; + UINT16 CFIVariant __WLAN_ATTRIB_PACK__; + UINT16 CFIBottom __WLAN_ATTRIB_PACK__; + UINT16 CFITop __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_CFIActRanges_t; + +/*-- Information Record: NICSerialNumber --*/ +typedef struct hfa384x_NICSerialNumber +{ + UINT8 NICSerialNumber[12] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_NICSerialNumber_t; + +/*-- Information Record: NICIdentity --*/ +typedef struct hfa384x_NICIdentity +{ + UINT16 NICCompID __WLAN_ATTRIB_PACK__; + UINT16 NICVariant __WLAN_ATTRIB_PACK__; + UINT16 NICMajorVersion __WLAN_ATTRIB_PACK__; + UINT16 NICMinorVersion __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_NICIdentity_t; + +/*-- Information Record: MFISupRange --*/ +typedef struct hfa384x_MFISupRange +{ + UINT16 MFIRole __WLAN_ATTRIB_PACK__; + UINT16 MFIID __WLAN_ATTRIB_PACK__; + UINT16 MFIVariant __WLAN_ATTRIB_PACK__; + UINT16 MFIBottom __WLAN_ATTRIB_PACK__; + UINT16 MFITop __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_MFISupRange_t; + +/*-- Information Record: CFISupRange --*/ +typedef struct hfa384x_CFISupRange +{ + UINT16 CFIRole __WLAN_ATTRIB_PACK__; + UINT16 CFIID __WLAN_ATTRIB_PACK__; + UINT16 CFIVariant __WLAN_ATTRIB_PACK__; + UINT16 CFIBottom __WLAN_ATTRIB_PACK__; + UINT16 CFITop __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_CFISupRange_t; + +/*-- Information Record: BUILDSEQ:BuildSeq --*/ +typedef struct hfa384x_BuildSeq { + UINT16 primary __WLAN_ATTRIB_PACK__; + UINT16 secondary __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_BuildSeq_t; + +/*-- Information Record: FWID --*/ +#define HFA384x_FWID_LEN 14 +typedef struct hfa384x_FWID { + UINT8 primary[HFA384x_FWID_LEN] __WLAN_ATTRIB_PACK__; + UINT8 secondary[HFA384x_FWID_LEN] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_FWID_t; + +/*-- Information Record: ChannelList --*/ +typedef struct hfa384x_ChannelList +{ + UINT16 ChannelList __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_ChannelList_t; + +/*-- Information Record: RegulatoryDomains --*/ +typedef struct hfa384x_RegulatoryDomains +{ + UINT8 RegulatoryDomains[12] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_RegulatoryDomains_t; + +/*-- Information Record: TempType --*/ +typedef struct hfa384x_TempType +{ + UINT16 TempType __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_TempType_t; + +/*-- Information Record: CIS --*/ +typedef struct hfa384x_CIS +{ + UINT8 CIS[480] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_CIS_t; + +/*-- Information Record: STAIdentity --*/ +typedef struct hfa384x_STAIdentity +{ + UINT16 STACompID __WLAN_ATTRIB_PACK__; + UINT16 STAVariant __WLAN_ATTRIB_PACK__; + UINT16 STAMajorVersion __WLAN_ATTRIB_PACK__; + UINT16 STAMinorVersion __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_STAIdentity_t; + +/*-- Information Record: STASupRange --*/ +typedef struct hfa384x_STASupRange +{ + UINT16 STARole __WLAN_ATTRIB_PACK__; + UINT16 STAID __WLAN_ATTRIB_PACK__; + UINT16 STAVariant __WLAN_ATTRIB_PACK__; + UINT16 STABottom __WLAN_ATTRIB_PACK__; + UINT16 STATop __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_STASupRange_t; + +/*-- Information Record: MFIActRanges --*/ +typedef struct hfa384x_MFIActRanges +{ + UINT16 MFIRole __WLAN_ATTRIB_PACK__; + UINT16 MFIID __WLAN_ATTRIB_PACK__; + UINT16 MFIVariant __WLAN_ATTRIB_PACK__; + UINT16 MFIBottom __WLAN_ATTRIB_PACK__; + UINT16 MFITop __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_MFIActRanges_t; + +/*-------------------------------------------------------------------- +Information Record Structures: NIC Information +--------------------------------------------------------------------*/ + +/*-- Information Record: PortStatus --*/ +typedef struct hfa384x_PortStatus +{ + UINT16 PortStatus __WLAN_ATTRIB_PACK__; +} __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__; +} __WLAN_ATTRIB_PACK__ hfa384x_CurrentSSID_t; + +/*-- Information Record: CurrentBSSID --*/ +typedef struct hfa384x_CurrentBSSID +{ + UINT8 CurrentBSSID[6] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_CurrentBSSID_t; + +/*-- Information Record: commsquality --*/ +typedef struct hfa384x_commsquality +{ + UINT16 CQ_currBSS __WLAN_ATTRIB_PACK__; + UINT16 ASL_currBSS __WLAN_ATTRIB_PACK__; + UINT16 ANL_currFC __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_commsquality_t; + +/*-- Information Record: dmbcommsquality --*/ +typedef struct hfa384x_dbmcommsquality +{ + UINT16 CQdbm_currBSS __WLAN_ATTRIB_PACK__; + UINT16 ASLdbm_currBSS __WLAN_ATTRIB_PACK__; + UINT16 ANLdbm_currFC __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_dbmcommsquality_t; + +/*-- Information Record: CurrentTxRate --*/ +typedef struct hfa384x_CurrentTxRate +{ + UINT16 CurrentTxRate __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_CurrentTxRate_t; + +/*-- Information Record: CurrentBeaconInterval --*/ +typedef struct hfa384x_CurrentBeaconInterval +{ + UINT16 CurrentBeaconInterval __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_CurrentBeaconInterval_t; + +/*-- Information Record: CurrentScaleThresholds --*/ +typedef struct hfa384x_CurrentScaleThresholds +{ + UINT16 EnergyDetectThreshold __WLAN_ATTRIB_PACK__; + UINT16 CarrierDetectThreshold __WLAN_ATTRIB_PACK__; + UINT16 DeferDetectThreshold __WLAN_ATTRIB_PACK__; + UINT16 CellSearchThreshold __WLAN_ATTRIB_PACK__; /* Stations only */ + UINT16 DeadSpotThreshold __WLAN_ATTRIB_PACK__; /* Stations only */ +} __WLAN_ATTRIB_PACK__ hfa384x_CurrentScaleThresholds_t; + +/*-- Information Record: ProtocolRspTime --*/ +typedef struct hfa384x_ProtocolRspTime +{ + UINT16 ProtocolRspTime __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_ProtocolRspTime_t; + +/*-- Information Record: ShortRetryLimit --*/ +typedef struct hfa384x_ShortRetryLimit +{ + UINT16 ShortRetryLimit __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_ShortRetryLimit_t; + +/*-- Information Record: LongRetryLimit --*/ +typedef struct hfa384x_LongRetryLimit +{ + UINT16 LongRetryLimit __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_LongRetryLimit_t; + +/*-- Information Record: MaxTransmitLifetime --*/ +typedef struct hfa384x_MaxTransmitLifetime +{ + UINT16 MaxTransmitLifetime __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_MaxTransmitLifetime_t; + +/*-- Information Record: MaxReceiveLifetime --*/ +typedef struct hfa384x_MaxReceiveLifetime +{ + UINT16 MaxReceiveLifetime __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_MaxReceiveLifetime_t; + +/*-- Information Record: CFPollable --*/ +typedef struct hfa384x_CFPollable +{ + UINT16 CFPollable __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_CFPollable_t; + +/*-- Information Record: AuthenticationAlgorithms --*/ +typedef struct hfa384x_AuthenticationAlgorithms +{ + UINT16 AuthenticationType __WLAN_ATTRIB_PACK__; + UINT16 TypeEnabled __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_AuthenticationAlgorithms_t; + +/*-- Information Record: AuthenticationAlgorithms +(data only --*/ +typedef struct hfa384x_AuthenticationAlgorithms_data +{ + UINT16 AuthenticationType __WLAN_ATTRIB_PACK__; + UINT16 TypeEnabled __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_AuthenticationAlgorithms_data_t; + +/*-- Information Record: PrivacyOptionImplemented --*/ +typedef struct hfa384x_PrivacyOptionImplemented +{ + UINT16 PrivacyOptionImplemented __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_PrivacyOptionImplemented_t; + +/*-- Information Record: OwnMACAddress --*/ +typedef struct hfa384x_OwnMACAddress +{ + UINT8 OwnMACAddress[6] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_OwnMACAddress_t; + +/*-- Information Record: PCFInfo --*/ +typedef struct hfa384x_PCFInfo +{ + UINT16 MediumOccupancyLimit __WLAN_ATTRIB_PACK__; + UINT16 CFPPeriod __WLAN_ATTRIB_PACK__; + UINT16 CFPMaxDuration __WLAN_ATTRIB_PACK__; + UINT16 CFPFlags __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_PCFInfo_t; + +/*-- Information Record: PCFInfo (data portion only) --*/ +typedef struct hfa384x_PCFInfo_data +{ + UINT16 MediumOccupancyLimit __WLAN_ATTRIB_PACK__; + UINT16 CFPPeriod __WLAN_ATTRIB_PACK__; + UINT16 CFPMaxDuration __WLAN_ATTRIB_PACK__; + UINT16 CFPFlags __WLAN_ATTRIB_PACK__; +} __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__; +} __WLAN_ATTRIB_PACK__ hfa384x_PHYType_t; + +/*-- Information Record: CurrentChannel --*/ +typedef struct hfa384x_CurrentChannel +{ + UINT16 CurrentChannel __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_CurrentChannel_t; + +/*-- Information Record: CurrentPowerState --*/ +typedef struct hfa384x_CurrentPowerState +{ + UINT16 CurrentPowerState __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_CurrentPowerState_t; + +/*-- Information Record: CCAMode --*/ +typedef struct hfa384x_CCAMode +{ + UINT16 CCAMode __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_CCAMode_t; + +/*-- Information Record: SupportedDataRates --*/ +typedef struct hfa384x_SupportedDataRates +{ + UINT8 SupportedDataRates[10] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_SupportedDataRates_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 __WLAN_ATTRIB_PACK__; + UINT16 reserved1 __WLAN_ATTRIB_PACK__; + UINT16 reserved2 __WLAN_ATTRIB_PACK__; + UINT32 sw_support __WLAN_ATTRIB_PACK__; + UINT16 reserved3 __WLAN_ATTRIB_PACK__; + UINT16 tx_control __WLAN_ATTRIB_PACK__; + + /*-- 802.11 Header Information --*/ + + UINT16 frame_control __WLAN_ATTRIB_PACK__; + UINT16 duration_id __WLAN_ATTRIB_PACK__; + UINT8 address1[6] __WLAN_ATTRIB_PACK__; + UINT8 address2[6] __WLAN_ATTRIB_PACK__; + UINT8 address3[6] __WLAN_ATTRIB_PACK__; + UINT16 sequence_control __WLAN_ATTRIB_PACK__; + UINT8 address4[6] __WLAN_ATTRIB_PACK__; + UINT16 data_len __WLAN_ATTRIB_PACK__; /* little endian format */ + + /*-- 802.3 Header Information --*/ + + UINT8 dest_addr[6] __WLAN_ATTRIB_PACK__; + UINT8 src_addr[6] __WLAN_ATTRIB_PACK__; + UINT16 data_length __WLAN_ATTRIB_PACK__; /* 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 __WLAN_ATTRIB_PACK__; + UINT32 time __WLAN_ATTRIB_PACK__; + UINT8 silence __WLAN_ATTRIB_PACK__; + UINT8 signal __WLAN_ATTRIB_PACK__; + UINT8 rate __WLAN_ATTRIB_PACK__; + UINT8 rx_flow __WLAN_ATTRIB_PACK__; + UINT16 reserved1 __WLAN_ATTRIB_PACK__; + UINT16 reserved2 __WLAN_ATTRIB_PACK__; + + /*-- 802.11 Header Information (802.11 byte order) --*/ + UINT16 frame_control __WLAN_ATTRIB_PACK__; + UINT16 duration_id __WLAN_ATTRIB_PACK__; + UINT8 address1[6] __WLAN_ATTRIB_PACK__; + UINT8 address2[6] __WLAN_ATTRIB_PACK__; + UINT8 address3[6] __WLAN_ATTRIB_PACK__; + UINT16 sequence_control __WLAN_ATTRIB_PACK__; + UINT8 address4[6] __WLAN_ATTRIB_PACK__; + UINT16 data_len __WLAN_ATTRIB_PACK__; /* hfa384x (little endian) format */ + + /*-- 802.3 Header Information --*/ + UINT8 dest_addr[6] __WLAN_ATTRIB_PACK__; + UINT8 src_addr[6] __WLAN_ATTRIB_PACK__; + UINT16 data_length __WLAN_ATTRIB_PACK__; /* 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_COMMTALLIES ((UINT16)0xF100UL) +#define HFA384x_IT_SCANRESULTS ((UINT16)0xF101UL) +#define HFA384x_IT_CHINFORESULTS ((UINT16)0xF102UL) +#define HFA384x_IT_HOSTSCANRESULTS ((UINT16)0xF103UL)//NEW +#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)//NEW AP + +/*-------------------------------------------------------------------- +Information Frames Structures +---------------------------------------------------------------------- +Information Frames: Notification Frame Structures +--------------------------------------------------------------------*/ +/*-- Notification Frame,MAC Mgmt: Handover Address --*/ +typedef struct hfa384x_HandoverAddr +{ + UINT16 framelen __WLAN_ATTRIB_PACK__; + UINT16 infotype __WLAN_ATTRIB_PACK__; + UINT8 handover_addr[WLAN_BSSID_LEN] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_HandoverAddr_t; + +/*-- Inquiry Frame, Diagnose: Communication Tallies --*/ +typedef struct __WLAN_ATTRIB_PACK__ hfa384x_CommTallies16 +{ + UINT16 txunicastframes __WLAN_ATTRIB_PACK__; + UINT16 txmulticastframes __WLAN_ATTRIB_PACK__; + UINT16 txfragments __WLAN_ATTRIB_PACK__; + UINT16 txunicastoctets __WLAN_ATTRIB_PACK__; + UINT16 txmulticastoctets __WLAN_ATTRIB_PACK__; + UINT16 txdeferredtrans __WLAN_ATTRIB_PACK__; + UINT16 txsingleretryframes __WLAN_ATTRIB_PACK__; + UINT16 txmultipleretryframes __WLAN_ATTRIB_PACK__; + UINT16 txretrylimitexceeded __WLAN_ATTRIB_PACK__; + UINT16 txdiscards __WLAN_ATTRIB_PACK__; + UINT16 rxunicastframes __WLAN_ATTRIB_PACK__; + UINT16 rxmulticastframes __WLAN_ATTRIB_PACK__; + UINT16 rxfragments __WLAN_ATTRIB_PACK__; + UINT16 rxunicastoctets __WLAN_ATTRIB_PACK__; + UINT16 rxmulticastoctets __WLAN_ATTRIB_PACK__; + UINT16 rxfcserrors __WLAN_ATTRIB_PACK__; + UINT16 rxdiscardsnobuffer __WLAN_ATTRIB_PACK__; + UINT16 txdiscardswrongsa __WLAN_ATTRIB_PACK__; + UINT16 rxdiscardswepundecr __WLAN_ATTRIB_PACK__; + UINT16 rxmsginmsgfrag __WLAN_ATTRIB_PACK__; + UINT16 rxmsginbadmsgfrag __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_CommTallies16_t; + +typedef struct __WLAN_ATTRIB_PACK__ hfa384x_CommTallies32 +{ + UINT32 txunicastframes __WLAN_ATTRIB_PACK__; + UINT32 txmulticastframes __WLAN_ATTRIB_PACK__; + UINT32 txfragments __WLAN_ATTRIB_PACK__; + UINT32 txunicastoctets __WLAN_ATTRIB_PACK__; + UINT32 txmulticastoctets __WLAN_ATTRIB_PACK__; + UINT32 txdeferredtrans __WLAN_ATTRIB_PACK__; + UINT32 txsingleretryframes __WLAN_ATTRIB_PACK__; + UINT32 txmultipleretryframes __WLAN_ATTRIB_PACK__; + UINT32 txretrylimitexceeded __WLAN_ATTRIB_PACK__; + UINT32 txdiscards __WLAN_ATTRIB_PACK__; + UINT32 rxunicastframes __WLAN_ATTRIB_PACK__; + UINT32 rxmulticastframes __WLAN_ATTRIB_PACK__; + UINT32 rxfragments __WLAN_ATTRIB_PACK__; + UINT32 rxunicastoctets __WLAN_ATTRIB_PACK__; + UINT32 rxmulticastoctets __WLAN_ATTRIB_PACK__; + UINT32 rxfcserrors __WLAN_ATTRIB_PACK__; + UINT32 rxdiscardsnobuffer __WLAN_ATTRIB_PACK__; + UINT32 txdiscardswrongsa __WLAN_ATTRIB_PACK__; + UINT32 rxdiscardswepundecr __WLAN_ATTRIB_PACK__; + UINT32 rxmsginmsgfrag __WLAN_ATTRIB_PACK__; + UINT32 rxmsginbadmsgfrag __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_CommTallies32_t; + +/*-- Inquiry Frame, Diagnose: Scan Results & Subfields--*/ +typedef struct hfa384x_ScanResultSub +{ + UINT16 chid __WLAN_ATTRIB_PACK__; + UINT16 anl __WLAN_ATTRIB_PACK__; + UINT16 sl __WLAN_ATTRIB_PACK__; + UINT8 bssid[WLAN_BSSID_LEN] __WLAN_ATTRIB_PACK__; + UINT16 bcnint __WLAN_ATTRIB_PACK__; + UINT16 capinfo __WLAN_ATTRIB_PACK__; + hfa384x_bytestr32_t ssid __WLAN_ATTRIB_PACK__; + UINT8 supprates[10] __WLAN_ATTRIB_PACK__; /* 802.11 info element */ + UINT16 proberesp_rate __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_ScanResultSub_t; + +typedef struct hfa384x_ScanResult +{ + UINT16 rsvd __WLAN_ATTRIB_PACK__; + UINT16 scanreason __WLAN_ATTRIB_PACK__; + hfa384x_ScanResultSub_t + result[HFA384x_SCANRESULT_MAX] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_ScanResult_t; + +/*-- Inquiry Frame, Diagnose: ChInfo Results & Subfields--*/ +typedef struct hfa384x_ChInfoResultSub +{ + UINT16 chid __WLAN_ATTRIB_PACK__; + UINT16 anl __WLAN_ATTRIB_PACK__; + UINT16 pnl __WLAN_ATTRIB_PACK__; + UINT16 active __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_ChInfoResultSub_t; + +#define HFA384x_CHINFORESULT_BSSACTIVE BIT0 +#define HFA384x_CHINFORESULT_PCFACTIVE BIT1 + +typedef struct hfa384x_ChInfoResult +{ + UINT16 scanchannels __WLAN_ATTRIB_PACK__; + hfa384x_ChInfoResultSub_t + result[HFA384x_CHINFORESULT_MAX] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_ChInfoResult_t; + +/*-- Inquiry Frame, Diagnose: Host Scan Results & Subfields--*/ +typedef struct hfa384x_HScanResultSub +{ + UINT16 chid __WLAN_ATTRIB_PACK__; + UINT16 anl __WLAN_ATTRIB_PACK__; + UINT16 sl __WLAN_ATTRIB_PACK__; + UINT8 bssid[WLAN_BSSID_LEN] __WLAN_ATTRIB_PACK__; + UINT16 bcnint __WLAN_ATTRIB_PACK__; + UINT16 capinfo __WLAN_ATTRIB_PACK__; + hfa384x_bytestr32_t ssid __WLAN_ATTRIB_PACK__; + UINT8 supprates[10] __WLAN_ATTRIB_PACK__; /* 802.11 info element */ + UINT16 proberesp_rate __WLAN_ATTRIB_PACK__; + UINT16 atim __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_HScanResultSub_t; + +typedef struct hfa384x_HScanResult +{ + UINT16 nresult __WLAN_ATTRIB_PACK__; + UINT16 rsvd __WLAN_ATTRIB_PACK__; + hfa384x_HScanResultSub_t + result[HFA384x_HSCANRESULT_MAX] __WLAN_ATTRIB_PACK__; +} __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__; +} __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 __WLAN_ATTRIB_PACK__; + UINT8 sta_addr[WLAN_ADDR_LEN] __WLAN_ATTRIB_PACK__; + /* old_ap_addr is only valid if assocstatus == 2 */ + UINT8 old_ap_addr[WLAN_ADDR_LEN] __WLAN_ATTRIB_PACK__; + UINT16 reason __WLAN_ATTRIB_PACK__; + UINT16 reserved __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_AssocStatus_t; + +/*-- Unsolicited Frame, MAC Mgmt: AuthRequest (AP Only) --*/ + +typedef struct hfa384x_AuthRequest +{ + UINT8 sta_addr[WLAN_ADDR_LEN] __WLAN_ATTRIB_PACK__; + UINT16 algorithm __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_AuthReq_t; + +/*-- Unsolicited Frame, MAC Mgmt: PSUserCount (AP Only) --*/ + +typedef struct hfa384x_PSUserCount +{ + UINT16 usercnt __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_PSUserCount_t; + +/*-- Collection of all Inf frames ---------------*/ +typedef union hfa384x_infodata { + hfa384x_CommTallies16_t commtallies16 __WLAN_ATTRIB_PACK__; + hfa384x_CommTallies32_t commtallies32 __WLAN_ATTRIB_PACK__; + hfa384x_ScanResult_t scanresult __WLAN_ATTRIB_PACK__; + hfa384x_ChInfoResult_t chinforesult __WLAN_ATTRIB_PACK__; + hfa384x_HScanResult_t hscanresult __WLAN_ATTRIB_PACK__; + hfa384x_LinkStatus_t linkstatus __WLAN_ATTRIB_PACK__; + hfa384x_AssocStatus_t assocstatus __WLAN_ATTRIB_PACK__; + hfa384x_AuthReq_t authreq __WLAN_ATTRIB_PACK__; + hfa384x_PSUserCount_t psusercnt __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_infodata_t; + +typedef struct hfa384x_InfFrame +{ + UINT16 framelen __WLAN_ATTRIB_PACK__; + UINT16 infotype __WLAN_ATTRIB_PACK__; + hfa384x_infodata_t info __WLAN_ATTRIB_PACK__; +} __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) < 0x7fff) +#define HFA384x_USB_ISTXFRM(a) (HFA384x_USB_ISFRM((a)) && ((a) & 0x1000)) +#define HFA384x_USB_ISRXFRM(a) (HFA384x_USB_ISFRM((a)) && !((a) & 0x1000)) +#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 __WLAN_ATTRIB_PACK__; + UINT8 data[WLAN_DATA_MAXLEN] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_usb_txfrm_t; + +typedef struct hfa384x_usb_cmdreq { + UINT16 type __WLAN_ATTRIB_PACK__; + UINT16 cmd __WLAN_ATTRIB_PACK__; + UINT16 parm0 __WLAN_ATTRIB_PACK__; + UINT16 parm1 __WLAN_ATTRIB_PACK__; + UINT16 parm2 __WLAN_ATTRIB_PACK__; + UINT8 pad[54] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_usb_cmdreq_t; + +typedef struct hfa384x_usb_wridreq { + UINT16 type __WLAN_ATTRIB_PACK__; + UINT16 frmlen __WLAN_ATTRIB_PACK__; + UINT16 rid __WLAN_ATTRIB_PACK__; + UINT8 data[HFA384x_RIDDATA_MAXLEN] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_usb_wridreq_t; + +typedef struct hfa384x_usb_rridreq { + UINT16 type __WLAN_ATTRIB_PACK__; + UINT16 frmlen __WLAN_ATTRIB_PACK__; + UINT16 rid __WLAN_ATTRIB_PACK__; + UINT8 pad[58] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_usb_rridreq_t; + +typedef struct hfa384x_usb_wmemreq { + UINT16 type __WLAN_ATTRIB_PACK__; + UINT16 frmlen __WLAN_ATTRIB_PACK__; + UINT16 offset __WLAN_ATTRIB_PACK__; + UINT16 page __WLAN_ATTRIB_PACK__; + UINT8 data[HFA384x_USB_RWMEM_MAXLEN] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_usb_wmemreq_t; + +typedef struct hfa384x_usb_rmemreq { + UINT16 type __WLAN_ATTRIB_PACK__; + UINT16 frmlen __WLAN_ATTRIB_PACK__; + UINT16 offset __WLAN_ATTRIB_PACK__; + UINT16 page __WLAN_ATTRIB_PACK__; + UINT8 pad[56] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_usb_rmemreq_t; + +/*------------------------------------*/ +/* Response (bulk IN) packet contents */ + +typedef struct hfa384x_usb_rxfrm { + hfa384x_rx_frame_t desc __WLAN_ATTRIB_PACK__; + UINT8 data[WLAN_DATA_MAXLEN] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_usb_rxfrm_t; + +typedef struct hfa384x_usb_infofrm { + UINT16 type __WLAN_ATTRIB_PACK__; + hfa384x_InfFrame_t info __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_usb_infofrm_t; + +typedef struct hfa384x_usb_cmdresp { + UINT16 type __WLAN_ATTRIB_PACK__; + UINT16 status __WLAN_ATTRIB_PACK__; + UINT16 resp0 __WLAN_ATTRIB_PACK__; + UINT16 resp1 __WLAN_ATTRIB_PACK__; + UINT16 resp2 __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_usb_cmdresp_t; + +typedef struct hfa384x_usb_wridresp { + UINT16 type __WLAN_ATTRIB_PACK__; + UINT16 status __WLAN_ATTRIB_PACK__; + UINT16 resp0 __WLAN_ATTRIB_PACK__; + UINT16 resp1 __WLAN_ATTRIB_PACK__; + UINT16 resp2 __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_usb_wridresp_t; + +typedef struct hfa384x_usb_rridresp { + UINT16 type __WLAN_ATTRIB_PACK__; + UINT16 frmlen __WLAN_ATTRIB_PACK__; + UINT16 rid __WLAN_ATTRIB_PACK__; + UINT8 data[HFA384x_RIDDATA_MAXLEN] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_usb_rridresp_t; + +typedef struct hfa384x_usb_wmemresp { + UINT16 type __WLAN_ATTRIB_PACK__; + UINT16 status __WLAN_ATTRIB_PACK__; + UINT16 resp0 __WLAN_ATTRIB_PACK__; + UINT16 resp1 __WLAN_ATTRIB_PACK__; + UINT16 resp2 __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_usb_wmemresp_t; + +typedef struct hfa384x_usb_rmemresp { + UINT16 type __WLAN_ATTRIB_PACK__; + UINT16 frmlen __WLAN_ATTRIB_PACK__; + UINT8 data[HFA384x_USB_RWMEM_MAXLEN] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_usb_rmemresp_t; + +typedef struct hfa384x_usb_bufavail { + UINT16 type __WLAN_ATTRIB_PACK__; + UINT16 frmlen __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_usb_bufavail_t; + +typedef struct hfa384x_usb_error { + UINT16 type __WLAN_ATTRIB_PACK__; + UINT16 errortype __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_usb_error_t; + +/*----------------------------------------------------------*/ +/* Unions for packaging all the known packet types together */ + +typedef union hfa384x_usbout { + UINT16 type __WLAN_ATTRIB_PACK__; + hfa384x_usb_txfrm_t txfrm __WLAN_ATTRIB_PACK__; + hfa384x_usb_cmdreq_t cmdreq __WLAN_ATTRIB_PACK__; + hfa384x_usb_wridreq_t wridreq __WLAN_ATTRIB_PACK__; + hfa384x_usb_rridreq_t rridreq __WLAN_ATTRIB_PACK__; + hfa384x_usb_wmemreq_t wmemreq __WLAN_ATTRIB_PACK__; + hfa384x_usb_rmemreq_t rmemreq __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_usbout_t; + +typedef union hfa384x_usbin { + UINT16 type __WLAN_ATTRIB_PACK__; + hfa384x_usb_rxfrm_t rxfrm __WLAN_ATTRIB_PACK__; + hfa384x_usb_txfrm_t txfrm __WLAN_ATTRIB_PACK__; + hfa384x_usb_infofrm_t infofrm __WLAN_ATTRIB_PACK__; + hfa384x_usb_cmdresp_t cmdresp __WLAN_ATTRIB_PACK__; + hfa384x_usb_wridresp_t wridresp __WLAN_ATTRIB_PACK__; + hfa384x_usb_rridresp_t rridresp __WLAN_ATTRIB_PACK__; + hfa384x_usb_wmemresp_t wmemresp __WLAN_ATTRIB_PACK__; + hfa384x_usb_rmemresp_t rmemresp __WLAN_ATTRIB_PACK__; + hfa384x_usb_bufavail_t bufavail __WLAN_ATTRIB_PACK__; + hfa384x_usb_error_t usberror __WLAN_ATTRIB_PACK__; + UINT8 boguspad[3000] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_usbin_t; + +#endif /* WLAN_USB */ + +/*-------------------------------------------------------------------- +PD record structures. +--------------------------------------------------------------------*/ + +typedef struct hfa384x_pdr_pcb_partnum +{ + UINT8 num[8] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_pcb_partnum_t; + +typedef struct hfa384x_pdr_pcb_tracenum +{ + UINT8 num[8] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_pcb_tracenum_t; + +typedef struct hfa384x_pdr_nic_serial +{ + UINT8 num[12] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_nic_serial_t; + +typedef struct hfa384x_pdr_mkk_measurements +{ + double carrier_freq __WLAN_ATTRIB_PACK__; + double occupied_band __WLAN_ATTRIB_PACK__; + double power_density __WLAN_ATTRIB_PACK__; + double tx_spur_f1 __WLAN_ATTRIB_PACK__; + double tx_spur_f2 __WLAN_ATTRIB_PACK__; + double tx_spur_f3 __WLAN_ATTRIB_PACK__; + double tx_spur_f4 __WLAN_ATTRIB_PACK__; + double tx_spur_l1 __WLAN_ATTRIB_PACK__; + double tx_spur_l2 __WLAN_ATTRIB_PACK__; + double tx_spur_l3 __WLAN_ATTRIB_PACK__; + double tx_spur_l4 __WLAN_ATTRIB_PACK__; + double rx_spur_f1 __WLAN_ATTRIB_PACK__; + double rx_spur_f2 __WLAN_ATTRIB_PACK__; + double rx_spur_l1 __WLAN_ATTRIB_PACK__; + double rx_spur_l2 __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_mkk_measurements_t; + +typedef struct hfa384x_pdr_nic_ramsize +{ + UINT8 size[12] __WLAN_ATTRIB_PACK__; /* units of KB */ +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_nic_ramsize_t; + +typedef struct hfa384x_pdr_mfisuprange +{ + UINT16 id __WLAN_ATTRIB_PACK__; + UINT16 variant __WLAN_ATTRIB_PACK__; + UINT16 bottom __WLAN_ATTRIB_PACK__; + UINT16 top __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_mfisuprange_t; + +typedef struct hfa384x_pdr_cfisuprange +{ + UINT16 id __WLAN_ATTRIB_PACK__; + UINT16 variant __WLAN_ATTRIB_PACK__; + UINT16 bottom __WLAN_ATTRIB_PACK__; + UINT16 top __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_cfisuprange_t; + +typedef struct hfa384x_pdr_nicid +{ + UINT16 id __WLAN_ATTRIB_PACK__; + UINT16 variant __WLAN_ATTRIB_PACK__; + UINT16 major __WLAN_ATTRIB_PACK__; + UINT16 minor __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_nicid_t; + + +typedef struct hfa384x_pdr_refdac_measurements +{ + UINT16 value[0] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_refdac_measurements_t; + +typedef struct hfa384x_pdr_vgdac_measurements +{ + UINT16 value[0] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_vgdac_measurements_t; + +typedef struct hfa384x_pdr_level_comp_measurements +{ + UINT16 value[0] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_level_compc_measurements_t; + +typedef struct hfa384x_pdr_mac_address +{ + UINT8 addr[6] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_mac_address_t; + +typedef struct hfa384x_pdr_mkk_callname +{ + UINT8 callname[8] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_mkk_callname_t; + +typedef struct hfa384x_pdr_regdomain +{ + UINT16 numdomains __WLAN_ATTRIB_PACK__; + UINT16 domain[5] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_regdomain_t; + +typedef struct hfa384x_pdr_allowed_channel +{ + UINT16 ch_bitmap __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_allowed_channel_t; + +typedef struct hfa384x_pdr_default_channel +{ + UINT16 channel __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_default_channel_t; + +typedef struct hfa384x_pdr_privacy_option +{ + UINT16 available __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_privacy_option_t; + +typedef struct hfa384x_pdr_temptype +{ + UINT16 type __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_temptype_t; + +typedef struct hfa384x_pdr_refdac_setup +{ + UINT16 ch_value[14] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_refdac_setup_t; + +typedef struct hfa384x_pdr_vgdac_setup +{ + UINT16 ch_value[14] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_vgdac_setup_t; + +typedef struct hfa384x_pdr_level_comp_setup +{ + UINT16 ch_value[14] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_level_comp_setup_t; + +typedef struct hfa384x_pdr_trimdac_setup +{ + UINT16 trimidac __WLAN_ATTRIB_PACK__; + UINT16 trimqdac __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_trimdac_setup_t; + +typedef struct hfa384x_pdr_ifr_setting +{ + UINT16 value[3] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_ifr_setting_t; + +typedef struct hfa384x_pdr_rfr_setting +{ + UINT16 value[3] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_rfr_setting_t; + +typedef struct hfa384x_pdr_hfa3861_baseline +{ + UINT16 value[50] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_hfa3861_baseline_t; + +typedef struct hfa384x_pdr_hfa3861_shadow +{ + UINT32 value[32] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_hfa3861_shadow_t; + +typedef struct hfa384x_pdr_hfa3861_ifrf +{ + UINT32 value[20] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_hfa3861_ifrf_t; + +typedef struct hfa384x_pdr_hfa3861_chcalsp +{ + UINT16 value[14] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_hfa3861_chcalsp_t; + +typedef struct hfa384x_pdr_hfa3861_chcali +{ + UINT16 value[17] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_hfa3861_chcali_t; + +typedef struct hfa384x_pdr_hfa3861_nic_config +{ + UINT16 config_bitmap __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_nic_config_t; + +typedef struct hfa384x_pdr_hfa3861_manf_testsp +{ + UINT16 value[30] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_hfa3861_manf_testsp_t; + +typedef struct hfa384x_pdr_hfa3861_manf_testi +{ + UINT16 value[30] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_hfa3861_manf_testi_t; + +typedef struct hfa384x_end_of_pda +{ + UINT16 crc __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_end_of_pda_t; + +typedef struct hfa384x_pdrec +{ + UINT16 len __WLAN_ATTRIB_PACK__; /* in words */ + UINT16 code __WLAN_ATTRIB_PACK__; + union pdr { + hfa384x_pdr_pcb_partnum_t pcb_partnum __WLAN_ATTRIB_PACK__; + hfa384x_pdr_pcb_tracenum_t pcb_tracenum __WLAN_ATTRIB_PACK__; + hfa384x_pdr_nic_serial_t nic_serial __WLAN_ATTRIB_PACK__; + hfa384x_pdr_mkk_measurements_t mkk_measurements __WLAN_ATTRIB_PACK__; + hfa384x_pdr_nic_ramsize_t nic_ramsize __WLAN_ATTRIB_PACK__; + hfa384x_pdr_mfisuprange_t mfisuprange __WLAN_ATTRIB_PACK__; + hfa384x_pdr_cfisuprange_t cfisuprange __WLAN_ATTRIB_PACK__; + hfa384x_pdr_nicid_t nicid __WLAN_ATTRIB_PACK__; + hfa384x_pdr_refdac_measurements_t refdac_measurements __WLAN_ATTRIB_PACK__; + hfa384x_pdr_vgdac_measurements_t vgdac_measurements __WLAN_ATTRIB_PACK__; + hfa384x_pdr_level_compc_measurements_t level_compc_measurements __WLAN_ATTRIB_PACK__; + hfa384x_pdr_mac_address_t mac_address __WLAN_ATTRIB_PACK__; + hfa384x_pdr_mkk_callname_t mkk_callname __WLAN_ATTRIB_PACK__; + hfa384x_pdr_regdomain_t regdomain __WLAN_ATTRIB_PACK__; + hfa384x_pdr_allowed_channel_t allowed_channel __WLAN_ATTRIB_PACK__; + hfa384x_pdr_default_channel_t default_channel __WLAN_ATTRIB_PACK__; + hfa384x_pdr_privacy_option_t privacy_option __WLAN_ATTRIB_PACK__; + hfa384x_pdr_temptype_t temptype __WLAN_ATTRIB_PACK__; + hfa384x_pdr_refdac_setup_t refdac_setup __WLAN_ATTRIB_PACK__; + hfa384x_pdr_vgdac_setup_t vgdac_setup __WLAN_ATTRIB_PACK__; + hfa384x_pdr_level_comp_setup_t level_comp_setup __WLAN_ATTRIB_PACK__; + hfa384x_pdr_trimdac_setup_t trimdac_setup __WLAN_ATTRIB_PACK__; + hfa384x_pdr_ifr_setting_t ifr_setting __WLAN_ATTRIB_PACK__; + hfa384x_pdr_rfr_setting_t rfr_setting __WLAN_ATTRIB_PACK__; + hfa384x_pdr_hfa3861_baseline_t hfa3861_baseline __WLAN_ATTRIB_PACK__; + hfa384x_pdr_hfa3861_shadow_t hfa3861_shadow __WLAN_ATTRIB_PACK__; + hfa384x_pdr_hfa3861_ifrf_t hfa3861_ifrf __WLAN_ATTRIB_PACK__; + hfa384x_pdr_hfa3861_chcalsp_t hfa3861_chcalsp __WLAN_ATTRIB_PACK__; + hfa384x_pdr_hfa3861_chcali_t hfa3861_chcali __WLAN_ATTRIB_PACK__; + hfa384x_pdr_nic_config_t nic_config __WLAN_ATTRIB_PACK__; + hfa384x_pdr_hfa3861_manf_testsp_t hfa3861_manf_testsp __WLAN_ATTRIB_PACK__; + hfa384x_pdr_hfa3861_manf_testi_t hfa3861_manf_testi __WLAN_ATTRIB_PACK__; + hfa384x_pdr_end_of_pda_t end_of_pda __WLAN_ATTRIB_PACK__; + } data __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ hfa384x_pdrec_t; + + +#ifdef __KERNEL__ +/*-------------------------------------------------------------------- +--- MAC state structure, argument to all functions -- +--- Also, a collection of support types -- +--------------------------------------------------------------------*/ + +struct hfa384x; /* forward declaration,grrr */ + +typedef void (*ctlx_usercb_t)( + struct hfa384x *hw, + UINT result, + void *ctlxresult, + void *usercb_data); + + +#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_async_* structures are arguments to + * the usercb() for the different CTLX types. + */ +typedef struct hfa384x_async_cmdresult +{ + UINT16 status; + UINT16 resp0; + UINT16 resp1; + UINT16 resp2; +} hfa384x_async_cmdresult_t; + +typedef struct hfa384x_async_rridresult +{ + UINT16 rid; + void *riddata; + UINT riddata_len; +} hfa384x_async_rridresult_t; + +typedef struct hfa384x_async_wridresult +{ + UINT16 status; + UINT16 resp0; + UINT16 resp1; + UINT16 resp2; +} hfa384x_async_wridresult_t; + +typedef struct hfa384x_async_rmemresult +{ +} hfa384x_async_rmemresult_t; + +typedef struct hfa384x_async_wmemresult +{ +} hfa384x_async_wmemresult_t; + + +typedef struct hfa384x_usbctlx +{ + struct hfa384x_usbctlx *prev, *next; + struct urb outurb; /* OUT for req pkt */ + struct urb inurb; /* IN for resp pkt */ + hfa384x_usbout_t outbuf; /* pkt buf for OUT */ + hfa384x_usbin_t inbuf; /* pkt buf for IN(a copy) */ + struct timer_list reqtimer; /* For IN(response)wait */ + struct timer_list resptimer; /* For OUT(request) wait */ + volatile UINT32 state; /* Tracks running state */ + int wanna_wakeup; /* Flag to wakeup sync calls */ + int is_async; /* Q'd by sync or async call */ + ctlx_usercb_t usercb; /* Async user callback, */ + void *usercb_data; /* at CTLX completion */ + int variant; /* Identifies cmd variant */ +} hfa384x_usbctlx_t; + +/* hfa384x_usbcmd_t.state values */ +#define HFA384x_USBCTLX_START 9 /* Start state, not Q'd */ +#define HFA384x_USBCTLX_QUEUED 1 /* Queued, data valid */ +#define HFA384x_USBCTLX_REQ_SUBMITTED 2 /* OUT URB submitted */ +#define HFA384x_USBCTLX_REQ_COMPLETE 3 /* OUT URB complete */ +#define HFA384x_USBCTLX_RESP_RECEIVED 4 /* IN URB received */ +#define HFA384x_USBCTLX_REQ_TIMEOUT 5 /* Timer expired waiting for OUT cb*/ +#define HFA384x_USBCTLX_REQ_FAILED 6 /* OUT URB completed w/ error */ +#define HFA384x_USBCTLX_RESP_TIMEOUT 7 /* Timer expired waiting for IN cb */ +#define HFA384x_USBCTLX_REQSUBMIT_FAIL 8 /* Timer expired waiting for IN cb */ +#define HFA384x_USBCTLX_COMPLETE 0 /* Exchange successfully complete */ + +typedef struct hfa384x_usbctlxq +{ + spinlock_t lock; + hfa384x_usbctlx_t *head; + hfa384x_usbctlx_t *tail; +} hfa384x_usbctlxq_t; +#endif + +typedef struct hfa484x_metacmd +{ + UINT16 cmd; + + UINT16 parm0; + UINT16 parm1; + UINT16 parm2; + + UINT16 status; /* in host order */ + + UINT16 resp0; /* in host order */ + UINT16 resp1; /* in host order */ + UINT16 resp2; /* in host order */ +#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_metacmd_t; + +typedef struct hfa384x +{ +#if (WLAN_HOSTIF != WLAN_USB) + /* Resource config */ + UINT32 iobase; + UINT32 membase; + UINT32 irq; +#else + /* USB support data */ + struct usb_device *usb; + void *usbcontext; /* actually a wlandev */ + struct urb rx_urb; + struct urb tx_urb; + struct urb int_urb; + hfa384x_usbin_t rxbuff; + hfa384x_usbout_t txbuff; + UINT16 intbuff[4]; + int rxurb_posted; + hfa384x_usbctlxq_t ctlxq; + int endp_in; + int endp_out; +#endif /* !USB */ + + int sniff_fcs; + int sniff_channel; + int sniff_truncate; + + wait_queue_head_t cmdq; /* wait queue itself */ + + /* Controller state */ + UINT32 state; + UINT32 hwremoved; + UINT32 isap; + UINT8 port_enabled[HFA384x_NUMPORTS_MAX]; +#if (WLAN_HOSTIF != WLAN_USB) + UINT auxen; +#endif /* !USB */ + + /* Download support */ + UINT dlstate; + hfa384x_downloadbuffer_t bufinfo; + UINT16 dltimeout; + +#if (WLAN_HOSTIF != WLAN_USB) + spinlock_t cmdlock; + int cmdflag; /* wait queue flag */ + hfa384x_metacmd_t *cmddata; /* for our async callback */ + + /* BAP support */ + spinlock_t baplock; + + /* MAC buffer ids */ + spinlock_t txfid_lock; + 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 */ +} hfa384x_t; + +/*=============================================================*/ +/*--- Function Declarations -----------------------------------*/ +/*=============================================================*/ +#if (WLAN_HOSTIF == WLAN_USB) +void +hfa384x_create( + hfa384x_t *hw, + struct usb_device *usb, + void *usbcontext); +#else +void +hfa384x_create( + hfa384x_t *hw, + UINT irq, + UINT32 iobase, + UINT32 membase); +#endif + +void hfa384x_destroy(hfa384x_t *hw); + +void hfa384x_hwremoved(hfa384x_t *hw); + +void +hfa384x_interrupt( + int irq, + void *dev_id, + struct pt_regs *regs); +int +hfa384x_corereset( hfa384x_t *hw, int holdtime, int settletime); +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_getconfig16(hfa384x_t *hw, UINT16 rid, void *val); +int +hfa384x_drvr_getconfig32(hfa384x_t *hw, UINT16 rid, void *val); +void +hfa384x_drvr_getconfig_async( + hfa384x_t *hw, + UINT16 rid, + ctlx_usercb_t usercb, + void *usercb_data); +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); +int +hfa384x_drvr_setconfig16(hfa384x_t *hw, UINT16 rid, UINT16 *val); +int +hfa384x_drvr_setconfig32(hfa384x_t *hw, UINT16 rid, UINT32 *val); +void +hfa384x_drvr_setconfig_async( + hfa384x_t *hw, + UINT16 rid, + void *buf, + UINT16 len, + ctlx_usercb_t usercb, + void *usercb_data); +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); + + +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 +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 +} + +#endif /* WLAN_HOSTIF != WLAN_USB */ +#endif /* __KERNEL__ */ + +#endif /* _HFA384x_H */ diff --git a/src/drivers/net/mtd80x.c b/src/drivers/net/mtd80x.c new file mode 100644 index 00000000..a09fdfba --- /dev/null +++ b/src/drivers/net/mtd80x.c @@ -0,0 +1,1096 @@ +/************************************************************************** +* +* mtd80x.c: Etherboot device driver for the mtd80x Ethernet chip. +* Written 2004-2004 by Erdem Güven +* +* 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 "pci.h" + +#if 0 +#define DBGPRNT( x ) printf x +#else +#define DBGPRNT( x ) +#endif + +typedef unsigned char u8; +typedef signed char s8; +typedef unsigned short u16; +typedef signed short s16; +typedef unsigned int u32; +typedef signed int s32; + +/* 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 '%'<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, +}; + +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 ); + +/* 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 +TX Descriptor. All descriptors point to a +part of this buffer */ +static u8 txb[PKT_BUF_SZ * TX_RING_SIZE] +__attribute__ ((aligned(8))); + +/* 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[PKT_BUF_SZ * RX_RING_SIZE] +__attribute__ ((aligned(8))); + +/* 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 mdio_write(struct nic * , int phy_id, int location, int value); +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) +{ + 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) + { + char* texts[]={"half","full","10","100","1000"}; + getlinktype(nic); + DBGPRNT(("Link is OK : %s %s\n", texts[mtdx.duplexmode-1], texts[mtdx.line_speed+1] )); + } else + { + DBGPRNT(("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) +{ + 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, 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; + + DBGPRNT(( " netdev_rx() normal Rx pkt length %d" + " status %x.\n", pkt_len, 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){ + DBGPRNT(("TX Time Out")); + } else if( tx_status & (CSL | LC | EC | UDF | HF)){ + printf("Transmit error: %s %s %s %s %s.\n", + 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();*/ + + DBGPRNT(("TRANSMIT\n")); +} + +/************************************************************************** +DISABLE - Turn off ethernet interface +***************************************************************************/ +static void mtd_disable(struct dev *dev) +{ + /* put the card in its initial state */ + /* Disable Tx Rx*/ + outl( mtdx.crvalue & (~TxEnable) & (~RxEnable), mtdx.ioaddr + TCRRCR); + /* Reset the chip to erase previous misconfiguration. */ + mtd_reset((struct nic *) dev); + DBGPRNT(("DISABLE\n")); +} + +/************************************************************************** +PROBE - Look for an adapter, this routine's visible to the outside +***************************************************************************/ + +static int mtd_probe(struct dev *dev, struct pci_device *pci) +{ + struct nic *nic = (struct nic *)dev; + int i; + + if (pci->ioaddr == 0) + { + return 0; + } + + printf(" - "); + + /* Mask the bit that says "this is an io addr" */ + mtdx.ioaddr = pci->ioaddr & ~3; + + adjust_pci_device(pci); + + mtdx.nic_name = pci->name; + mtdx.dev_id = pci->dev_id; + + /* 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", 6) == 0) + { + return 0; + } + + DBGPRNT(("%s : ioaddr %#hX, addr %!\n",mtdx.nic_name, mtdx.ioaddr, 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; + + DBGPRNT(("%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; + DBGPRNT(("MysonPHY\n")); + } else { + mtdx.PHYType = OtherPHY; + DBGPRNT(("OtherPHY\n")); + } + } + + getlinkstatus(nic); + if( !mtdx.linkok ) + { + printf("No link!!!\n"); + return 0; + } + + mtd_reset( nic ); + + /* point to NIC specific routines */ + dev->disable = mtd_disable; + nic->poll = mtd_poll; + nic->transmit = mtd_transmit; + return 1; +} + +static struct pci_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"), + }; + +/**************************************************************************/ +static void set_rx_mode(struct nic *nic) +{ + 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, 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; +} + +static void mdio_write(struct nic *nic, 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; +} + +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; + } +} + + +struct pci_driver mtd80x_driver __pci_driver ={ + .type = NIC_DRIVER, + .name = "MTD80X", + .probe = mtd_probe, + .ids = mtd80x_nics, + .id_count = sizeof(mtd80x_nics)/sizeof(mtd80x_nics[0]), + .class = 0, +}; diff --git a/src/drivers/net/natsemi.c b/src/drivers/net/natsemi.c new file mode 100644 index 00000000..73c1368d --- /dev/null +++ b/src/drivers/net/natsemi.c @@ -0,0 +1,780 @@ +/* -*- Mode:C; c-basic-offset:4; -*- */ + +/* + 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@thinguin.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 */ + +/* + 13 Dec 2003 timlegge 1.1 Enabled Multicast Support + 29 May 2001 mdc 1.0 + Initial Release. Tested with Netgear FA311 and FA312 boards +*/ +/* Includes */ + +#include "etherboot.h" +#include "nic.h" +#include "pci.h" + +/* defines */ + +#define OWN 0x80000000 +#define DSIZE 0x00000FFF +#define CRC_SIZE 4 + +/* Time in ticks before concluding the transmitter is hung. */ +#define TX_TIMEOUT (4*TICKS_PER_SEC) + +#define TX_BUF_SIZE 1536 +#define RX_BUF_SIZE 1536 + +#define NUM_RX_DESC 4 /* Number of Rx descriptor registers. */ + +typedef uint8_t u8; +typedef int8_t s8; +typedef uint16_t u16; +typedef int16_t s16; +typedef uint32_t u32; +typedef int32_t s32; + +/* helpful macroes 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) + +enum pcistuff { + PCI_USES_IO = 0x01, + PCI_USES_MEM = 0x02, + PCI_USES_MASTER = 0x04, + PCI_ADDR0 = 0x08, + PCI_ADDR1 = 0x10, +}; + +/* MMIO operations required */ +#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_MEM | PCI_ADDR1) + +/* 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 +}; + +/* Bit in ChipCmd. */ +enum ChipCmdBits { + ChipReset = 0x100, + RxReset = 0x20, + TxReset = 0x10, + RxOff = 0x08, + RxOn = 0x04, + TxOff = 0x02, + TxOn = 0x01 +}; + +/* 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 +}; + +typedef struct _BufferDesc { + u32 link; + volatile u32 cmdsts; + u32 bufptr; + u32 software_use; +} BufferDesc; + +/* Bits in network_desc.status */ +enum desc_status_bits { + DescOwn = 0x80000000, + DescMore = 0x40000000, + DescIntr = 0x20000000, + DescNoCRC = 0x10000000, + DescPktOK = 0x08000000, + RxTooLong = 0x00400000 +}; + +/* Globals */ + +static int natsemi_debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */ + +const char *nic_name; + +static u32 SavedClkRun; + + +static unsigned short vendor, dev_id; +static unsigned long ioaddr; + +static unsigned int cur_rx; + +static unsigned int advertising; + +static unsigned int rx_config; +static unsigned int tx_config; + +/* Note: transmit and receive buffers and descriptors must be + longword aligned +*/ + +static BufferDesc txd __attribute__ ((aligned(4))); +static BufferDesc rxd[NUM_RX_DESC] __attribute__ ((aligned(4))); + +static unsigned char txb[TX_BUF_SIZE] __attribute__ ((aligned(4))); +static unsigned char rxb[NUM_RX_DESC * RX_BUF_SIZE] __attribute__ ((aligned(4))); + +/* Function Prototypes */ + +static int natsemi_probe(struct dev *dev, struct pci_device *pci); +static int eeprom_read(long addr, int location); +static int mdio_read(int phy_id, int location); +static void natsemi_init(struct nic *nic); +static void natsemi_reset(struct nic *nic); +static void natsemi_init_rxfilter(struct nic *nic); +static void natsemi_init_txd(struct nic *nic); +static void natsemi_init_rxd(struct nic *nic); +static void natsemi_set_rx_mode(struct nic *nic); +static void natsemi_check_duplex(struct nic *nic); +static void natsemi_transmit(struct nic *nic, const char *d, unsigned int t, unsigned int s, const char *p); +static int natsemi_poll(struct nic *nic, int retrieve); +static void natsemi_disable(struct dev *dev); +static void natsemi_irq(struct nic *nic, irq_action_t action); + +/* + * Function: natsemi_probe + * + * Description: Retrieves the MAC address of the card, and sets up some + * globals required by other routines, and initializes the NIC, making it + * ready to send and receive packets. + * + * Side effects: + * leaves the ioaddress of the natsemi chip in the variable ioaddr. + * leaves the natsemi initialized, and ready to recieve packets. + * + * Returns: struct nic *: pointer to NIC data structure + */ + +static int +natsemi_probe(struct dev *dev, struct pci_device *pci) +{ + struct nic *nic = (struct nic *)dev; + int i; + int prev_eedata; + u32 tmp; + + if (pci->ioaddr == 0) + return 0; + + adjust_pci_device(pci); + + /* initialize some commonly used globals */ + + nic->irqno = 0; + nic->ioaddr = pci->ioaddr & ~3; + + ioaddr = pci->ioaddr & ~3; + vendor = pci->vendor; + dev_id = pci->dev_id; + nic_name = pci->name; + + /* natsemi has a non-standard PM control register + * in PCI config space. Some boards apparently need + * to be brought to D0 in this manner. + */ + pcibios_read_config_dword(pci->bus, pci->devfn, PCIPM, &tmp); + if (tmp & (0x03|0x100)) { + /* D0 state, disable PME assertion */ + u32 newtmp = tmp & ~(0x03|0x100); + pcibios_write_config_dword(pci->bus, pci->devfn, PCIPM, newtmp); + } + + /* get MAC address */ + + prev_eedata = eeprom_read(ioaddr, 6); + for (i = 0; i < 3; i++) { + int eedata = eeprom_read(ioaddr, i + 7); + nic->node_addr[i*2] = (eedata << 1) + (prev_eedata >> 15); + nic->node_addr[i*2+1] = eedata >> 7; + prev_eedata = eedata; + } + + printf("\nnatsemi_probe: MAC addr %! at ioaddr %#hX\n", + nic->node_addr, ioaddr); + printf("natsemi_probe: Vendor:%#hX Device:%#hX\n", vendor, dev_id); + + /* Reset the chip to erase any previous misconfiguration. */ + outl(ChipReset, ioaddr + ChipCmd); + + advertising = mdio_read(1, 4); + { + u32 chip_config = inl(ioaddr + ChipConfig); + printf("%s: Transceiver default autoneg. %s " + "10%s %s duplex.\n", + nic_name, + chip_config & 0x2000 ? "enabled, advertise" : "disabled, force", + chip_config & 0x4000 ? "0" : "", + chip_config & 0x8000 ? "full" : "half"); + } + printf("%s: Transceiver status %hX advertising %hX\n", + nic_name, (int)inl(ioaddr + 0x84), advertising); + + /* 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. */ + SavedClkRun = inl(ioaddr + ClkRun); + outl(SavedClkRun & ~0x100, ioaddr + ClkRun); + + /* initialize device */ + natsemi_init(nic); + + dev->disable = natsemi_disable; + nic->poll = natsemi_poll; + nic->transmit = natsemi_transmit; + nic->irq = natsemi_irq; + + return 1; +} + +/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. + The EEPROM code is for the common 93c06/46 EEPROMs with 6 bit addresses. +*/ + +/* 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), +}; + +static int eeprom_read(long addr, int location) +{ + int i; + int retval = 0; + int ee_addr = addr + EECtrl; + int read_cmd = location | EE_ReadCmd; + outl(EE_Write0, ee_addr); + + /* Shift the read command bits out. */ + for (i = 10; i >= 0; i--) { + short dataval = (read_cmd & (1 << i)) ? EE_Write1 : EE_Write0; + outl(dataval, ee_addr); + eeprom_delay(ee_addr); + outl(dataval | EE_ShiftClk, ee_addr); + eeprom_delay(ee_addr); + } + outl(EE_ChipSelect, ee_addr); + eeprom_delay(ee_addr); + + for (i = 0; i < 16; i++) { + outl(EE_ChipSelect | EE_ShiftClk, ee_addr); + eeprom_delay(ee_addr); + retval |= (inl(ee_addr) & EE_DataOut) ? 1 << i : 0; + outl(EE_ChipSelect, ee_addr); + eeprom_delay(ee_addr); + } + + /* Terminate the EEPROM access. */ + outl(EE_Write0, ee_addr); + outl(0, ee_addr); + + return retval; +} + +/* MII transceiver control section. + The 83815 series has an internal transceiver, and we present the + management registers as if they were MII connected. */ + +static int mdio_read(int phy_id, int location) +{ + if (phy_id == 1 && location < 32) + return inl(ioaddr + 0x80 + (location<<2)) & 0xffff; + else + return 0xffff; +} + +/* Function: natsemi_init + * + * Description: resets the ethernet controller chip and configures + * registers and data structures required for sending and receiving packets. + * + * Arguments: struct nic *nic: NIC data structure + * + * returns: void. + */ + +static void +natsemi_init(struct nic *nic) +{ + natsemi_reset(nic); + + /* 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(SavedClkRun & ~0x100, ioaddr + ClkRun); + + natsemi_init_rxfilter(nic); + + natsemi_init_txd(nic); + natsemi_init_rxd(nic); + + /* Initialize other registers. */ + /* Configure the PCI bus bursts and FIFO thresholds. */ + /* Configure for standard, in-spec Ethernet. */ + if (inl(ioaddr + ChipConfig) & 0x20000000) { /* Full duplex */ + tx_config = 0xD0801002; + rx_config = 0x10000020; + } else { + tx_config = 0x10801002; + rx_config = 0x0020; + } + outl(tx_config, ioaddr + TxConfig); + outl(rx_config, ioaddr + RxConfig); + + natsemi_check_duplex(nic); + natsemi_set_rx_mode(nic); + + outl(RxOn, ioaddr + ChipCmd); +} + +/* + * Function: natsemi_reset + * + * Description: soft resets the controller chip + * + * Arguments: struct nic *nic: NIC data structure + * + * Returns: void. + */ +static void +natsemi_reset(struct nic *nic __unused) +{ + outl(ChipReset, ioaddr + ChipCmd); + + /* On page 78 of the spec, they recommend some settings for "optimum + performance" to be done in sequence. These settings optimize some + of the 100Mbit autodetection circuitry. Also, we only want to do + this for rev C of the chip. + */ + if (inl(ioaddr + SiliconRev) == 0x302) { + outw(0x0001, ioaddr + PGSEL); + outw(0x189C, ioaddr + PMDCSR); + outw(0x0000, ioaddr + TSTDAT); + outw(0x5040, ioaddr + DSPCFG); + outw(0x008C, ioaddr + SDCFG); + } + /* Disable interrupts using the mask. */ + outl(0, ioaddr + IntrMask); + outl(0, ioaddr + IntrEnable); +} + +/* Function: natsemi_init_rxfilter + * + * Description: sets receive filter address to our MAC address + * + * Arguments: struct nic *nic: NIC data structure + * + * returns: void. + */ + +static void +natsemi_init_rxfilter(struct nic *nic) +{ + int i; + + for (i = 0; i < ETH_ALEN; i += 2) { + outl(i, ioaddr + RxFilterAddr); + outw(nic->node_addr[i] + (nic->node_addr[i+1] << 8), ioaddr + RxFilterData); + } +} + +/* + * Function: natsemi_init_txd + * + * Description: initializes the Tx descriptor + * + * Arguments: struct nic *nic: NIC data structure + * + * returns: void. + */ + +static void +natsemi_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 + TxRingPtr); + if (natsemi_debug > 1) + printf("natsemi_init_txd: TX descriptor register loaded with: %X\n", + inl(ioaddr + TxRingPtr)); +} + +/* Function: natsemi_init_rxd + * + * Description: initializes the Rx descriptor ring + * + * Arguments: struct nic *nic: NIC data structure + * + * Returns: void. + */ + +static void +natsemi_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 (natsemi_debug > 1) + printf("natsemi_init_rxd: rxd[%d]=%X link=%X cmdsts=%X bufptr=%X\n", + i, &rxd[i], rxd[i].link, rxd[i].cmdsts, rxd[i].bufptr); + } + + /* load Receive Descriptor Register */ + outl(virt_to_bus(&rxd[0]), ioaddr + RxRingPtr); + + if (natsemi_debug > 1) + printf("natsemi_init_rxd: RX descriptor register loaded with: %X\n", + inl(ioaddr + RxRingPtr)); +} + +/* Function: natsemi_set_rx_mode + * + * 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 natsemi_set_rx_mode(struct nic *nic __unused) +{ + u32 rx_mode = RxFilterEnable | AcceptBroadcast | + AcceptAllMulticast | AcceptMyPhys; + + outl(rx_mode, ioaddr + RxFilterAddr); +} + +static void natsemi_check_duplex(struct nic *nic __unused) +{ + int duplex = inl(ioaddr + ChipConfig) & 0x20000000 ? 1 : 0; + + if (natsemi_debug) + printf("%s: Setting %s-duplex based on negotiated link" + " capability.\n", nic_name, + duplex ? "full" : "half"); + if (duplex) { + rx_config |= 0x10000000; + tx_config |= 0xC0000000; + } else { + rx_config &= ~0x10000000; + tx_config &= ~0xC0000000; + } + outl(tx_config, ioaddr + TxConfig); + outl(rx_config, ioaddr + RxConfig); +} + +/* Function: natsemi_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 +natsemi_transmit(struct nic *nic, + const char *d, /* Destination */ + unsigned int t, /* Type */ + unsigned int s, /* size */ + const char *p) /* Packet */ +{ + u32 to, nstype; + u32 tx_status; + + /* Stop the transmitter */ + outl(TxOff, ioaddr + ChipCmd); + + /* load Transmit Descriptor Register */ + outl(virt_to_bus(&txd), ioaddr + TxRingPtr); + if (natsemi_debug > 1) + printf("natsemi_transmit: TX descriptor register loaded with: %X\n", + inl(ioaddr + TxRingPtr)); + + 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 (natsemi_debug > 1) + printf("natsemi_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(TxOn, ioaddr + ChipCmd); + + if (natsemi_debug > 1) + printf("natsemi_transmit: Queued Tx packet size %d.\n", (int) s); + + to = currticks() + TX_TIMEOUT; + + while ((((volatile u32) tx_status=txd.cmdsts) & OWN) && (currticks() < to)) + /* wait */ ; + + if (currticks() >= to) { + printf("natsemi_transmit: TX Timeout! Tx status %X.\n", tx_status); + } + + if (!(tx_status & 0x08000000)) { + printf("natsemi_transmit: Transmit error, Tx status %X.\n", tx_status); + } +} + +/* Function: natsemi_poll + * + * Description: checks for a received packet and returns it if found. + * + * Arguments: struct nic *nic: NIC data structure + * + * Returns: 1 if packet was received. + * 0 if no packet was received. + * + * Side effects: + * Returns (copies) the packet to the array nic->packet. + * Returns the length of the packet in nic->packetlen. + */ + +static int +natsemi_poll(struct nic *nic, int retrieve) +{ + u32 rx_status = rxd[cur_rx].cmdsts; + int retstat = 0; + + if (natsemi_debug > 2) + printf("natsemi_poll: cur_rx:%d, status:%X\n", cur_rx, rx_status); + + if (!(rx_status & OWN)) + return retstat; + + if ( ! retrieve ) return 1; + + if (natsemi_debug > 1) + printf("natsemi_poll: got a packet: cur_rx:%d, status:%X\n", + cur_rx, rx_status); + + nic->packetlen = (rx_status & DSIZE) - CRC_SIZE; + + if ((rx_status & (DescMore|DescPktOK|RxTooLong)) != DescPktOK) { + /* corrupted packet received */ + printf("natsemi_poll: Corrupted packet received, buffer status = %X\n", + 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(RxOn, ioaddr + ChipCmd); + + return retstat; +} + +/* Function: natsemi_disable + * + * Description: Turns off interrupts and stops Tx and Rx engines + * + * Arguments: struct nic *nic: NIC data structure + * + * Returns: void. + */ + +static void +natsemi_disable(struct dev *dev) +{ + struct nic *nic = (struct nic *)dev; + /* merge reset and disable */ + natsemi_init(nic); + + /* Disable interrupts using the mask. */ + outl(0, ioaddr + IntrMask); + outl(0, ioaddr + IntrEnable); + + /* Stop the chip's Tx and Rx processes. */ + outl(RxOff | TxOff, ioaddr + ChipCmd); + + /* Restore PME enable bit */ + outl(SavedClkRun, ioaddr + ClkRun); +} + +/* Function: natsemi_irq + * + * Description: Enable, Disable, or Force interrupts + * + * Arguments: struct nic *nic: NIC data structure + * irq_action_t action: requested action to perform + * + * Returns: void. + */ + +static void +natsemi_irq(struct nic *nic __unused, irq_action_t action __unused) +{ + switch ( action ) { + case DISABLE : + break; + case ENABLE : + break; + case FORCE : + break; + } +} + +static struct pci_id natsemi_nics[] = { +PCI_ROM(0x100b, 0x0020, "dp83815", "DP83815"), +}; + +static struct pci_driver natsemi_driver __pci_driver = { + .type = NIC_DRIVER, + .name = "NATSEMI", + .probe = natsemi_probe, + .ids = natsemi_nics, + .id_count = sizeof(natsemi_nics)/sizeof(natsemi_nics[0]), + .class = 0, +}; diff --git a/src/drivers/net/ns83820.c b/src/drivers/net/ns83820.c new file mode 100755 index 00000000..16b41f8b --- /dev/null +++ b/src/drivers/net/ns83820.c @@ -0,0 +1,1020 @@ +/************************************************************************** +* ns83820.c: Etherboot device driver for the National Semiconductor 83820 +* Written 2004 by Timothy Legge +* +* 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 "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 + +typedef unsigned char u8; +typedef signed char s8; +typedef unsigned short u16; +typedef signed short s16; +typedef unsigned int u32; +typedef signed int s32; + +#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 + +/* Define the TX Descriptor */ +static struct ring_desc tx_ring[NR_TX_DESC] + __attribute__ ((aligned(8))); + +/* Create a static buffer of size REAL_RX_BUF_SIZE for each +TX Descriptor. All descriptors point to a +part of this buffer */ +static unsigned char txb[NR_TX_DESC * REAL_RX_BUF_SIZE]; + +/* Define the TX Descriptor */ +static struct ring_desc rx_ring[NR_RX_DESC] + __attribute__ ((aligned(8))); + +/* Create a static buffer of size REAL_RX_BUF_SIZE for each +RX Descriptor All descriptors point to a +part of this buffer */ +static unsigned char rxb[NR_RX_DESC * REAL_RX_BUF_SIZE] + __attribute__ ((aligned(8))); + +/* Private Storage for the NIC */ +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; + +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, status, fail); + else if (timed_out) + printf("run_bist %s timed out! (%hX)\n", name, 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 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. + */ + /* disable interrupts */ + writel(0, ns->base + IMR); + writel(0, ns->base + IER); + readl(ns->base + IER); + + ns->up = 0; + + ns83820_do_reset((struct nic *) dev, 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; + } +} + +/************************************************************************** +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 dev *dev, struct pci_device *pci) +{ + struct nic *nic = (struct nic *) dev; + int sz; + 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->name, pci->vendor, pci->dev_id); + + /* point to private storage */ + ns = &nsx; + + adjust_pci_device(pci); + + addr = pci_bar_start(pci, PCI_BASE_ADDRESS_1); + sz = pci_bar_size(pci, PCI_BASE_ADDRESS_1); + + ns->base = ioremap(addr, (1UL << 12)); +// ns->base = ioremap(addr, sz); + + 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->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->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->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->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); + printf("%! at ioaddr 0x%hX, ", nic->node_addr, ns->base); + + if (using_dac) { + dprintf(("%s: using 64 bit addressing.\n", pci->name)); + } + + dprintf(("%s: DP83820 %d.%d: %! io=0x%hX\n", + pci->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 */ + dev->disable = ns83820_disable; + nic->poll = ns83820_poll; + nic->transmit = ns83820_transmit; + nic->irq = ns83820_irq; + return 1; +} + +static struct pci_id ns83820_nics[] = { + PCI_ROM(0x100b, 0x0022, "ns83820", "National Semiconductor 83820"), +}; + +static struct pci_driver ns83820_driver __pci_driver = { + .type = NIC_DRIVER, + .name = "NS83820/PCI", + .probe = ns83820_probe, + .ids = ns83820_nics, + .id_count = sizeof(ns83820_nics) / sizeof(ns83820_nics[0]), + .class = 0, +}; diff --git a/src/drivers/net/ns8390.c b/src/drivers/net/ns8390.c new file mode 100644 index 00000000..dddc0819 --- /dev/null +++ b/src/drivers/net/ns8390.c @@ -0,0 +1,1016 @@ +/************************************************************************** +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) + +**************************************************************************/ + +#include "etherboot.h" +#include "nic.h" +#include "ns8390.h" +#ifdef INCLUDE_NS8390 +#include "pci.h" +#else +#include "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; inode_addr[i], eth_nic_base+D8390_P1_PAR0+i); + for (i=0; iflags) ? 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(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(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, (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 dev *dev) +{ + struct nic *nic = (struct nic *)dev; + /* reset and disable merge */ + 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; + } +} + +/************************************************************************** +ETH_PROBE - Look for an adapter +**************************************************************************/ +#ifdef INCLUDE_NS8390 +static int eth_probe (struct dev *dev, struct pci_device *pci) +#else +static int eth_probe (struct dev *dev, unsigned short *probe_addrs __unused) +#endif +{ + struct nic *nic = (struct nic *)dev; + 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; inode_addr[i] = inb(i+eth_asic_base+WD_LAR); + } + printf("\n%s base %#hx", brd->name, eth_asic_base); + if (eth_flags & FLAG_790) { +#ifdef WD_790_PIO + printf(", PIO mode, addr %!\n", nic->node_addr); + eth_bmem = 0; + eth_flags |= FLAG_PIO; /* force PIO mode */ + outb(0, eth_asic_base+WD_MSR); +#else + printf(", memory %#x, addr %!\n", eth_bmem, 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 { + printf(", memory %#x, addr %!\n", eth_bmem, 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; + printf("\n3Com 3c503 base %#hx, ", eth_nic_base); + if (eth_flags & FLAG_PIO) + printf("PIO mode"); + else + printf("memory %#x", eth_bmem); + for (i=0; inode_addr[i] = inb(eth_nic_base+i); + } + printf(", %s, addr %!\n", nic->flags ? "AUI" : "internal xcvr", + 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) { + char romdata[16], testbuf[32]; + int idx; + static 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); + 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(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(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; inode_addr[i] = romdata[i + ((eth_flags & FLAG_16BIT) ? i : 0)]; + } + nic->ioaddr = eth_nic_base; + printf("\nNE%c000 base %#hx, addr %!\n", + (eth_flags & FLAG_16BIT) ? '2' : '1', eth_nic_base, + nic->node_addr); + } +} +#endif + if (eth_vendor == VENDOR_NONE) + return(0); + if (eth_vendor != VENDOR_3COM) + eth_rmem = eth_bmem; + ns8390_reset(nic); + + dev->disable = ns8390_disable; + nic->poll = ns8390_poll; + nic->transmit = ns8390_transmit; + nic->irq = ns8390_irq; + + /* 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 +static struct isa_driver wd_driver __isa_driver = { + .type = NIC_DRIVER, + .name = "WD", + .probe = wd_probe, + .ioaddrs = 0, +}; +#endif + +#ifdef INCLUDE_3C503 +static struct isa_driver t503_driver __isa_driver = { + .type = NIC_DRIVER, + .name = "3C503", + .probe = t503_probe, + .ioaddrs = 0, +}; +#endif + +#ifdef INCLUDE_NE +static struct isa_driver ne_driver __isa_driver = { + .type = NIC_DRIVER, + .name = "NE*000", + .probe = ne_probe, + .ioaddrs = 0, +}; +#endif + +#ifdef INCLUDE_NS8390 +static struct pci_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"), +}; + +static struct pci_driver nepci_driver __pci_driver = { + .type = NIC_DRIVER, + .name = "NE2000/PCI", + .probe = nepci_probe, + .ids = nepci_nics, + .id_count = sizeof(nepci_nics)/sizeof(nepci_nics[0]), + .class = 0, +}; + +#endif /* INCLUDE_NS8390 */ + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ + diff --git a/src/drivers/net/ns8390.h b/src/drivers/net/ns8390.h new file mode 100644 index 00000000..2c4e972d --- /dev/null +++ b/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/src/drivers/net/p80211hdr.h b/src/drivers/net/p80211hdr.h new file mode 100644 index 00000000..50d92796 --- /dev/null +++ b/src/drivers/net/p80211hdr.h @@ -0,0 +1,262 @@ +/* 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 +#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_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 */ +__WLAN_PRAGMA_PACK1__ +typedef struct p80211_hdr_a3 +{ + UINT16 fc __WLAN_ATTRIB_PACK__; + UINT16 dur __WLAN_ATTRIB_PACK__; + UINT8 a1[WLAN_ADDR_LEN] __WLAN_ATTRIB_PACK__; + UINT8 a2[WLAN_ADDR_LEN] __WLAN_ATTRIB_PACK__; + UINT8 a3[WLAN_ADDR_LEN] __WLAN_ATTRIB_PACK__; + UINT16 seq __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ p80211_hdr_a3_t; +__WLAN_PRAGMA_PACKDFLT__ + +__WLAN_PRAGMA_PACK1__ +typedef struct p80211_hdr_a4 +{ + UINT16 fc __WLAN_ATTRIB_PACK__; + UINT16 dur __WLAN_ATTRIB_PACK__; + UINT8 a1[WLAN_ADDR_LEN] __WLAN_ATTRIB_PACK__; + UINT8 a2[WLAN_ADDR_LEN] __WLAN_ATTRIB_PACK__; + UINT8 a3[WLAN_ADDR_LEN] __WLAN_ATTRIB_PACK__; + UINT16 seq __WLAN_ATTRIB_PACK__; + UINT8 a4[WLAN_ADDR_LEN] __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ p80211_hdr_a4_t; +__WLAN_PRAGMA_PACKDFLT__ + +typedef union p80211_hdr +{ + p80211_hdr_a3_t a3 __WLAN_ATTRIB_PACK__; + p80211_hdr_a4_t a4 __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ p80211_hdr_t; + + +/*================================================================*/ +/* Extern Declarations */ + + +/*================================================================*/ +/* Function Declarations */ + +void p802addr_to_str( char *buf, UINT8 *addr); + +#endif /* _P80211HDR_H */ diff --git a/src/drivers/net/pcnet32.c b/src/drivers/net/pcnet32.c new file mode 100644 index 00000000..c2fb897a --- /dev/null +++ b/src/drivers/net/pcnet32.c @@ -0,0 +1,1004 @@ +/************************************************************************** +* +* pcnet32.c -- Etherboot device driver for the AMD PCnet32 +* Written 2003-2003 by Timothy Legge +* +* 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 +***************************************************************************/ + +/* 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 "pci.h" +/* Include the time functions */ +#include "timer.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" + +typedef unsigned char u8; +typedef signed char s8; +typedef unsigned short u16; +typedef signed short s16; +typedef unsigned int u32; +typedef signed int s32; + +static u32 ioaddr; /* Globally used for the card's io address */ + +#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 */ + +int cards_found /* __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 + +/* Buffers for the tx and Rx */ + +/* Create a static buffer of size PKT_BUF_SZ for each +TX Descriptor. All descriptors point to a +part of this buffer */ +static unsigned char txb[PKT_BUF_SZ * TX_RING_SIZE]; +// __attribute__ ((aligned(16))); + +/* Create a static buffer of size PKT_BUF_SZ for each +RX Descriptor All descriptors point to a +part of this buffer */ +static unsigned char rxb[RX_RING_SIZE * PKT_BUF_SZ]; +// __attribute__ ((aligned(16))); + +/* 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 Descriptor */ +static struct pcnet32_tx_head tx_ring[TX_RING_SIZE] + __attribute__ ((aligned(16))); + + +/* Define the RX Descriptor */ +static struct pcnet32_rx_head rx_ring[RX_RING_SIZE] + __attribute__ ((aligned(16))); + +/* 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++) { + rx_ring[i].base = (u32) virt_to_le32desc(&rxb[i]); + rx_ring[i].buf_length = le16_to_cpu(-PKT_BUF_SZ); + 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++) { + tx_ring[i].base = 0; + 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 = (u32) virt_to_le32desc(&rx_ring[0]); + lp->init_block.tx_ring = (u32) virt_to_le32desc(&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 (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 */ + + int status; + int entry; + + entry = lp->cur_rx & RX_RING_MOD_MASK; + status = ((short) le16_to_cpu(rx_ring[entry].status) >> 8); + + if (status < 0) + return 0; + + if ( ! retrieve ) return 1; + + if (status == 0x03) { + nic->packetlen = + (le32_to_cpu(rx_ring[entry].msg_length) & 0xfff) - 4; + memcpy(nic->packet, &rxb[entry], nic->packetlen); + + /* Andrew Boyd of QNX reports that some revs of the 79C765 + * clear the buffer length */ + rx_ring[entry].buf_length = le16_to_cpu(-PKT_BUF_SZ); + rx_ring[entry].status |= le16_to_cpu(0x8000); /* prime for next receive */ + /* 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 = 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'; + + tx_ring[entry].length = le16_to_cpu(-s); + tx_ring[entry].misc = 0x00000000; + tx_ring[entry].base = (u32) virt_to_le32desc(ptxb); + + /* we set the top byte as the very last thing */ + 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(tx_ring[entry].status) < 0)); + + if ((short) le16_to_cpu(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 */ + tx_ring[entry].base = 0; + +} + +/************************************************************************** +DISABLE - Turn off ethernet interface +***************************************************************************/ +static void pcnet32_disable(struct dev *dev __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 problesm with dumb + * DOS packet driver after a warm reboot + */ + lp->a.write_bcr(ioaddr, 20, 4); +} + +/************************************************************************** +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 dev *dev, struct pci_device *pci) +{ + struct nic *nic = (struct nic *) dev; + int i, media; + int fdx, mii, fset, dxsuflo, ltint; + int chip_version; + char *chipname; + struct pcnet32_access *a = NULL; + 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->name, pci->vendor, pci->dev_id); + + 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 %0xhX\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: + 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; + } + + dprintf(("%s at %hX,", chipname, 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 */ + printf("%s: %! at ioaddr %hX, ", pci->name, nic->node_addr, + ioaddr); + + /* 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; + + /* 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(&rx_ring); + lp->init_block.tx_ring = virt_to_bus(&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 (1) { + 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"); + } + + nic->poll = pcnet32_poll; + nic->transmit = pcnet32_transmit; + dev->disable = pcnet32_disable; + nic->irq = pcnet32_irq; + + 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 pci_id pcnet32_nics[] = { + PCI_ROM(0x1022, 0x2000, "lancepci", "AMD Lance/PCI"), + PCI_ROM(0x1022, 0x2625, "pcnetfastiii", "AMD Lance/PCI PCNet/32"), + PCI_ROM(0x1022, 0x2001, "amdhomepna", "AMD Lance/HomePNA"), +}; + +static struct pci_driver pcnet32_driver __pci_driver = { + .type = NIC_DRIVER, + .name = "PCNET32/PCI", + .probe = pcnet32_probe, + .ids = pcnet32_nics, + .id_count = sizeof(pcnet32_nics) / sizeof(pcnet32_nics[0]), + .class = 0, +}; diff --git a/src/drivers/net/pnic.c b/src/drivers/net/pnic.c new file mode 100644 index 00000000..25c9cc1b --- /dev/null +++ b/src/drivers/net/pnic.c @@ -0,0 +1,267 @@ +/************************************************************************** +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. + */ + +/* 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 "pci.h" + +/* PNIC API */ +#include "pnic_api.h" + +/* Private data structure */ +typedef struct { + uint16_t api_version; +} pnic_priv_data_t; + +/* Function prototypes */ +static int pnic_api_check ( uint16_t api_version ); + +/* NIC specific static variables go here */ +static uint8_t tx_buffer[ETH_FRAME_LEN]; + +/* + * 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 nic *nic, uint16_t command, + void *input, uint16_t input_length, + void *output, uint16_t output_max_length, + uint16_t *output_length ) { + int i; + uint16_t status; + uint16_t _output_length; + + if ( input != NULL ) { + /* Write input length */ + outw ( input_length, nic->ioaddr + PNIC_REG_LEN ); + /* Write input data */ + for ( i = 0; i < input_length; i++ ) { + outb( ((char*)input)[i], nic->ioaddr + PNIC_REG_DATA ); + } + } + /* Write command */ + outw ( command, nic->ioaddr + PNIC_REG_CMD ); + /* Retrieve status */ + status = inw ( nic->ioaddr + PNIC_REG_STAT ); + /* Retrieve output length */ + _output_length = inw ( nic->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 */ + for ( i = 0; i < _output_length; i++ ) { + ((char*)output)[i] = + inb ( nic->ioaddr + PNIC_REG_DATA ); + } + } + return status; +} + +static uint16_t pnic_command ( struct nic *nic, uint16_t command, + void *input, uint16_t input_length, + void *output, uint16_t output_max_length, + uint16_t *output_length ) { + pnic_priv_data_t *priv = (pnic_priv_data_t*)nic->priv_data; + uint16_t status = pnic_command_quiet ( nic, 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 ); + if ( priv->api_version ) pnic_api_check(priv->api_version); + 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 int pnic_poll(struct nic *nic, int retrieve) +{ + uint16_t length; + uint16_t qlen; + + /* Check receive queue length to see if there's anything to + * get. Necessary since once we've called PNIC_CMD_RECV we + * have to read out the packet, otherwise it's lost forever. + */ + if ( pnic_command ( nic, PNIC_CMD_RECV_QLEN, NULL, 0, + &qlen, sizeof(qlen), NULL ) + != PNIC_STATUS_OK ) return ( 0 ); + if ( qlen == 0 ) return ( 0 ); + + /* There is a packet ready. Return 1 if we're only checking. */ + if ( ! retrieve ) return ( 1 ); + + /* Retrieve the packet */ + if ( pnic_command ( nic, PNIC_CMD_RECV, NULL, 0, + nic->packet, ETH_FRAME_LEN, &length ) + != PNIC_STATUS_OK ) return ( 0 ); + nic->packetlen = length; + return ( 1 ); +} + +/************************************************************************** +TRANSMIT - Transmit a frame +***************************************************************************/ +static void pnic_transmit( + struct nic *nic, + const char *dest, /* Destination */ + unsigned int type, /* Type */ + unsigned int size, /* size */ + const char *data) /* Packet */ +{ + unsigned int nstype = htons ( type ); + + if ( ( ETH_HLEN + size ) >= ETH_FRAME_LEN ) { + printf ( "pnic_transmit: packet too large\n" ); + return; + } + + /* Assemble packet */ + memcpy ( tx_buffer, dest, ETH_ALEN ); + memcpy ( tx_buffer + ETH_ALEN, nic->node_addr, ETH_ALEN ); + memcpy ( tx_buffer + 2 * ETH_ALEN, &nstype, 2 ); + memcpy ( tx_buffer + ETH_HLEN, data, size ); + + pnic_command ( nic, PNIC_CMD_XMIT, tx_buffer, ETH_HLEN + size, + NULL, 0, NULL ); +} + +/************************************************************************** +DISABLE - Turn off ethernet interface +***************************************************************************/ +static void pnic_disable(struct dev *dev) +{ + struct nic *nic = (struct nic *)dev; + pnic_command ( nic, PNIC_CMD_RESET, NULL, 0, NULL, 0, NULL ); +} + +/************************************************************************** +IRQ - Handle card interrupt status +***************************************************************************/ +static void pnic_irq ( struct nic *nic, irq_action_t action ) +{ + uint8_t enabled; + + switch ( action ) { + case DISABLE : + case ENABLE : + enabled = ( action == ENABLE ? 1 : 0 ); + pnic_command ( nic, PNIC_CMD_MASK_IRQ, + &enabled, sizeof(enabled), NULL, 0, NULL ); + break; + case FORCE : + pnic_command ( nic, PNIC_CMD_FORCE_IRQ, + NULL, 0, NULL, 0, NULL ); + break; + } +} + +/************************************************************************** +PROBE - Look for an adapter, this routine's visible to the outside +***************************************************************************/ + +static int pnic_probe(struct dev *dev, struct pci_device *pci) +{ + struct nic *nic = (struct nic *)dev; + static pnic_priv_data_t priv; + uint16_t status; + + printf(" - "); + + /* Clear private data structure and chain it in */ + memset ( &priv, 0, sizeof(priv) ); + nic->priv_data = &priv; + + /* Mask the bit that says "this is an io addr" */ + nic->ioaddr = pci->ioaddr & ~3; + nic->irqno = pci->irq; + /* Not sure what this does, but the rtl8139 driver does it */ + adjust_pci_device(pci); + + status = pnic_command_quiet( nic, PNIC_CMD_API_VER, NULL, 0, + &priv.api_version, + sizeof(priv.api_version), NULL ); + if ( status != PNIC_STATUS_OK ) { + printf ( "PNIC failed installation check, code %#hx\n", + status ); + return 0; + } + pnic_api_check(priv.api_version); + status = pnic_command ( nic, PNIC_CMD_READ_MAC, NULL, 0, + nic->node_addr, ETH_ALEN, NULL ); + printf ( "Detected Bochs Pseudo NIC MAC %! (API v%d.%d) at %#hx\n", + nic->node_addr, priv.api_version>>8, priv.api_version&0xff, + nic->ioaddr ); + + /* point to NIC specific routines */ + dev->disable = pnic_disable; + nic->poll = pnic_poll; + nic->transmit = pnic_transmit; + nic->irq = pnic_irq; + return 1; +} + +static struct pci_id pnic_nics[] = { +/* genrules.pl doesn't let us use macros for PCI IDs...*/ +PCI_ROM(0xfefe, 0xefef, "pnic", "Bochs Pseudo NIC Adaptor"), +}; + +static struct pci_driver pnic_driver __pci_driver = { + .type = NIC_DRIVER, + .name = "PNIC", + .probe = pnic_probe, + .ids = pnic_nics, + .id_count = sizeof(pnic_nics)/sizeof(pnic_nics[0]), + .class = 0, +}; diff --git a/src/drivers/net/pnic_api.h b/src/drivers/net/pnic_api.h new file mode 100644 index 00000000..6d117fa6 --- /dev/null +++ b/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/src/drivers/net/prism2.c b/src/drivers/net/prism2.c new file mode 100644 index 00000000..da010811 --- /dev/null +++ b/src/drivers/net/prism2.c @@ -0,0 +1,948 @@ +/************************************************************************** +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. + */ + +/* 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 "pci.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 +#include "timer.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) + +/* + * 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 __WLAN_ATTRIB_PACK__; + UINT8 ssap __WLAN_ATTRIB_PACK__; + UINT8 ctl __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ 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] __WLAN_ATTRIB_PACK__; + UINT16 type __WLAN_ATTRIB_PACK__; +} __WLAN_ATTRIB_PACK__ wlan_snap_t; + +typedef struct wlan_80211hdr +{ + wlan_llc_t llc; + wlan_snap_t snap; +} wlan_80211hdr_t; + +/* + * Function prototypes + */ + +#if (WLAN_HOSTIF == WLAN_PLX) +static int prism2_find_plx ( hfa384x_t *hw, struct pci_device *p ); +#elif (WLAN_HOSTIF == WLAN_PCI) +static int prism2_find_pci ( hfa384x_t *hw, struct pci_device *p ); +#endif + +/* + * 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*)(®))[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 dev *dev __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; + } +} + +/************************************************************************** +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 +***************************************************************************/ +#if (WLAN_HOSTIF == WLAN_PLX) +static int prism2_plx_probe(struct dev *dev, struct pci_device *p) +#elif (WLAN_HOSTIF == WLAN_PCI) +static int prism2_pci_probe(struct dev *dev, struct pci_device *p) +#endif +{ + struct nic *nic = (struct nic *)dev; + hfa384x_t *hw = &hw_global; + int result; + UINT16 tmp16 = 0; + UINT16 infofid; + hfa384x_InfFrame_t inf; + char ssid[HFA384x_RID_CNFDESIREDSSID_LEN]; + int info_count = 0; + + /* Find and intialise PLX Prism2 card */ +#if (WLAN_HOSTIF == WLAN_PLX) + if ( ! prism2_find_plx ( hw, p ) ) return 0; + nic->ioaddr = hw->iobase; +#elif (WLAN_HOSTIF == WLAN_PCI) + if ( ! prism2_find_pci ( hw, p ) ) return 0; + nic->ioaddr = hw->membase; +#endif + + 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 */ + + /* Retrieve MAC address (and fill out nic->node_addr) */ + hfa384x_drvr_getconfig ( hw, HFA384x_RID_CNFOWNMACADDR, nic->node_addr, HFA384x_RID_CNFOWNMACADDR_LEN ); + printf ( "MAC address %!\n", nic->node_addr ); + + /* 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 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); + printf ( "Link connected (BSSID %! - MAC address %!)\n", hw->bssid, nic->node_addr ); + + /* point to NIC specific routines */ + dev->disable = prism2_disable; + nic->poll = prism2_poll; + nic->transmit = prism2_transmit; + nic->irq = prism2_irq; + return 1; +} + +#if (WLAN_HOSTIF == WLAN_PLX) +/* + * 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 */ + pcibios_read_config_dword( p->bus, p->devfn, PLX_LOCAL_CONFIG_REGISTER_BASE, &plx_lcr); + plx_lcr &= PCI_BASE_ADDRESS_IO_MASK; + pcibios_read_config_dword( p->bus, p->devfn, PRISM2_PLX_ATTR_MEM_BASE, &attr_mem); + pcibios_read_config_dword( p->bus, p->devfn, 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 %#hx\n", plx_lcr ); + printf ( "Prism2 has attribute memory at %#x and I/O base at %#hx\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; +} +#endif /* WLAN_PLX */ + +#if (WLAN_HOSTIF == WLAN_PCI) +/* + * Find PCI card. + * + * Arguments: + * hw device structure to be filled in + * p PCI device structure + * + * Returns: + * 1 Success + */ +static int prism2_find_pci ( hfa384x_t *hw, struct pci_device *p ) +{ + uint32_t membase = 0; /* Prism2.5 Memory Base */ + pcibios_read_config_dword( p->bus, p->devfn, 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 %#x\n", hw->membase ); + return 1; +} +#endif /* WLAN_PCI */ + diff --git a/src/drivers/net/prism2_pci.c b/src/drivers/net/prism2_pci.c new file mode 100644 index 00000000..be2b06cc --- /dev/null +++ b/src/drivers/net/prism2_pci.c @@ -0,0 +1,32 @@ +/************************************************************************** +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. + */ + +#define WLAN_HOSTIF WLAN_PCI +#include "prism2.c" + +static struct pci_id prism2_pci_nics[] = { +PCI_ROM(0x1260, 0x3873, "prism2_pci", "Harris Semiconductor Prism2.5 clone"), /* Generic Prism2.5 PCI device */ +}; + +static struct pci_driver prism2_pci_driver __pci_driver = { + .type = NIC_DRIVER, + .name = "Prism2_PCI", + .probe = prism2_pci_probe, + .ids = prism2_pci_nics, + .id_count = sizeof(prism2_pci_nics)/sizeof(prism2_pci_nics[0]), + .class = 0, +}; + diff --git a/src/drivers/net/prism2_plx.c b/src/drivers/net/prism2_plx.c new file mode 100644 index 00000000..65954f75 --- /dev/null +++ b/src/drivers/net/prism2_plx.c @@ -0,0 +1,42 @@ +/************************************************************************** +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. + */ + +#define WLAN_HOSTIF WLAN_PLX +#include "prism2.c" + +static struct pci_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"), +}; + +static struct pci_driver prism2_plx_driver __pci_driver = { + .type = NIC_DRIVER, + .name = "Prism2_PLX", + .probe = prism2_plx_probe, + .ids = prism2_plx_nics, + .id_count = sizeof(prism2_plx_nics)/sizeof(prism2_plx_nics[0]), + .class = 0, +}; + diff --git a/src/drivers/net/r8169.c b/src/drivers/net/r8169.c new file mode 100644 index 00000000..83373dee --- /dev/null +++ b/src/drivers/net/r8169.c @@ -0,0 +1,854 @@ +/************************************************************************** +* r8169.c: Etherboot device driver for the RealTek RTL-8169 Gigabit +* Written 2003 by Timothy Legge +* +* 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 +* See Linux Driver for full information +* +* Linux Driver Version 1.27a, 10.02.2002 +* +* 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 +* +* 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 "pci.h" +#include "timer.h" + +#define drv_version "v1.6" +#define drv_date "03-27-2004" + +typedef unsigned char u8; +typedef signed char s8; +typedef unsigned short u16; +typedef signed short s16; +typedef unsigned int u32; +typedef signed int s32; + +#define HZ 1000 + +static u32 ioaddr; + +#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)) + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +/* 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 6 /* Maximum PCI burst, '6' is 1024 */ +#define TX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */ +#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) + +/* 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))) + +enum RTL8169_registers { + MAC0 = 0, /* Ethernet hardware address. */ + MAR0 = 8, /* 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, + EarlyTxThres = 0xEC, + FuncEvent = 0xF0, + FuncEventMask = 0xF4, + FuncPresetState = 0xF8, + FuncForceEvent = 0xFC, +}; + +enum RTL8169_register_content { + /*InterruptStatusBits */ + SYSErr = 0x8000, + PCSTimeout = 0x4000, + SWInt = 0x0100, + TxDescUnavail = 0x80, + RxFIFOOver = 0x40, + RxUnderrun = 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_Null = 0x0, + + /*_MediaType*/ + _10_Half = 0x01, + _10_Full = 0x02, + _100_Half = 0x04, + _100_Full = 0x08, + _1000_Full = 0x10, + + /*_TBICSRBit*/ + TBILinkOK = 0x02000000, +}; + +static struct { + const char *name; + u8 version; /* depend on RTL8169 docs */ + u32 RxConfigMask; /* should clear the bits supported by this chip */ +} rtl_chip_info[] = { + { +"RTL-8169", 0x00, 0xff7e1880,},}; + +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 */ + +/* Define the TX Descriptor */ +static u8 tx_ring[NUM_TX_DESC * sizeof(struct TxDesc) + 256]; + +/* Create a static buffer of size RX_BUF_SZ for each +TX Descriptor. All descriptors point to a +part of this buffer */ +static unsigned char txb[NUM_TX_DESC * RX_BUF_SIZE]; + +/* Define the RX Descriptor */ +static u8 rx_ring[NUM_RX_DESC * sizeof(struct TxDesc) + 256]; + +/* Create a static buffer of size RX_BUF_SZ for each +RX Descriptor All descriptors point to a +part of this buffer */ +static unsigned char rxb[NUM_RX_DESC * RX_BUF_SIZE]; + +struct rtl8169_private { + void *mmio_addr; /* memory map physical address */ + int chipset; + 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. */ + unsigned char *TxDescArrays; /* Index of Tx Descriptor buffer */ + unsigned char *RxDescArrays; /* Index of Rx Descriptor buffer */ + 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 = + SYSErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver | TxErr | + TxOK | RxErr | RxOK; +static const unsigned int rtl8169_rx_config = + (RX_FIFO_THRESH << RxCfgFIFOShift) | (RX_DMA_BURST << RxCfgDMAShift); + +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); + } + } +} + +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; +} + +static int rtl8169_init_board(struct pci_device *pdev) +{ + int i; + unsigned long rtreg_base, rtreg_len; + u32 tmp; + + rtreg_base = pci_bar_start(pdev, PCI_BASE_ADDRESS_1); + rtreg_len = pci_bar_size(pdev, PCI_BASE_ADDRESS_1); + + /* check for weird/broken PCI region reporting */ + if (rtreg_len < RTL_MIN_IO_SIZE) { + printf("Invalid PCI region size(s), aborting\n"); + } + + adjust_pci_device(pdev); +/* pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM); */ + + /* ioremap MMIO region */ + ioaddr = (unsigned long) ioremap(rtreg_base, rtreg_len); + if (ioaddr == 0) + return 0; + + 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 chip attached to board */ + tmp = RTL_R32(TxConfig); + tmp = ((tmp & 0x7c000000) + ((tmp & 0x00800000) << 2)) >> 24; + + for (i = ARRAY_SIZE(rtl_chip_info) - 1; i >= 0; i--) + if (tmp == rtl_chip_info[i].version) { + tpc->chipset = i; + goto match; + } + /* if unknown chip, assume array element #0, original RTL-8169 in this case */ + dprintf(("PCI device: unknown chip version, assuming RTL-8169\n")); + dprintf(("PCI device: TxConfig = 0x%hX\n", + (unsigned long) RTL_R32(TxConfig))); + tpc->chipset = 0; + return 1; + match: + return 0; + +} + +/************************************************************************** +IRQ - Wait for a frame +***************************************************************************/ +void r8169_irq ( struct nic *nic __unused, irq_action_t action ) { + int intr_status = 0; + int interested = RxUnderrun | 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(EarlyTxThres, EarlyTxThld); + + /* 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)); + + + 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); +} + +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; + u8 diff; + u32 TxPhyAddr, RxPhyAddr; + + tpc->TxDescArrays = tx_ring; + if (tpc->TxDescArrays == 0) + printf("Allot Error"); + /* Tx Desscriptor needs 256 bytes alignment; */ + TxPhyAddr = virt_to_bus(tpc->TxDescArrays); + diff = 256 - (TxPhyAddr - ((TxPhyAddr >> 8) << 8)); + TxPhyAddr += diff; + tpc->TxDescArray = (struct TxDesc *) (tpc->TxDescArrays + diff); + + tpc->RxDescArrays = rx_ring; + /* Rx Desscriptor needs 256 bytes alignment; */ + RxPhyAddr = virt_to_bus(tpc->RxDescArrays); + diff = 256 - (RxPhyAddr - ((RxPhyAddr >> 8) << 8)); + RxPhyAddr += diff; + tpc->RxDescArray = (struct RxDesc *) (tpc->RxDescArrays + diff); + + if (tpc->TxDescArrays == NULL || tpc->RxDescArrays == NULL) { + printf("Allocate RxDescArray or TxDescArray failed\n"); + return; + } + + 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 dev *dev __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->TxDescArrays = NULL; + tpc->RxDescArrays = NULL; + tpc->TxDescArray = NULL; + tpc->RxDescArray = NULL; + for (i = 0; i < NUM_RX_DESC; i++) { + tpc->RxBufferRing[i] = NULL; + } +} + +/************************************************************************** +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 dev *dev, struct pci_device *pci) +{ + struct nic *nic = (struct nic *) dev; + 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->name, pci->vendor, pci->dev_id); + + board_idx++; + + printed_version = 1; + + /* 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); + + dprintf(("%s: Identified chip type is '%s'.\n", pci->name, + rtl_chip_info[tpc->chipset].name)); + /* Print out some hardware info */ + printf("%s: %! at ioaddr %hX, ", pci->name, nic->node_addr, + ioaddr); + + /* if TBI is not endbled */ + if (!(RTL_R8(PHYstatus) & TBI_Enable)) { + int val = mdio_read(PHY_AUTO_NEGO_REG); + + 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; + } + /* leave PHY_AUTO_NEGO_REG bit4:0 unchanged */ + mdio_write(PHY_AUTO_NEGO_REG, + Cap10_100 | (val & 0x1F)); + mdio_write(PHY_1000_CTRL_REG, Cap1000); + } else { + dprintf(("Auto-negotiation Enabled.\n", + pci->name)); + + /* enable 10/100 Full/Half Mode, leave PHY_AUTO_NEGO_REG bit4:0 unchanged */ + mdio_write(PHY_AUTO_NEGO_REG, + PHY_Cap_10_Half | PHY_Cap_10_Full | + PHY_Cap_100_Half | PHY_Cap_100_Full | + (val & 0x1F)); + + /* enable 1000 Full Mode */ + mdio_write(PHY_1000_CTRL_REG, PHY_Cap_1000_Full); + + } + + /* Enable auto-negotiation and restart auto-nigotiation */ + mdio_write(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 (mdio_read(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 for-loop to wait for auto-negotiation process */ + + } else { + udelay(100); + printf + ("%s: 1000Mbps Full-duplex operation, TBI Link %s!\n", + pci->name, + (RTL_R32(TBICSR) & TBILinkOK) ? "OK" : "Failed"); + + } + + r8169_reset(nic); + /* point to NIC specific routines */ + dev->disable = r8169_disable; + nic->poll = r8169_poll; + nic->transmit = r8169_transmit; + nic->irqno = pci->irq; + nic->irq = r8169_irq; + nic->ioaddr = ioaddr; + return 1; + +} + +static struct pci_id r8169_nics[] = { + PCI_ROM(0x10ec, 0x8169, "r8169", "RealTek RTL8169 Gigabit Ethernet"), +}; + +static struct pci_driver r8169_driver __pci_driver = { + .type = NIC_DRIVER, + .name = "r8169/PCI", + .probe = r8169_probe, + .ids = r8169_nics, + .id_count = sizeof(r8169_nics) / sizeof(r8169_nics[0]), + .class = 0, +}; diff --git a/src/drivers/net/rtl8139.c b/src/drivers/net/rtl8139.c new file mode 100644 index 00000000..ee078d54 --- /dev/null +++ b/src/drivers/net/rtl8139.c @@ -0,0 +1,551 @@ +/* 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 */ +/*********************************************************************/ + +/* + 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@thinguin.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 "etherboot.h" +#include "nic.h" +#include "pci.h" +#include "timer.h" + +#define RTL_TIMEOUT (1*TICKS_PER_SEC) + +/* 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. */ +#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<node_addr; + + /* There are enough "RTL8139" strings on the console already, so + * be brief and concentrate on the interesting pieces of info... */ + printf(" - "); + + /* Mask the bit that says "this is an io addr" */ + nic->ioaddr = pci->ioaddr & ~3; + + /* Copy IRQ from PCI information */ + nic->irqno = pci->irq; + + adjust_pci_device(pci); + + /* Bring the chip out of low-power mode. */ + outb(0x00, nic->ioaddr + Config1); + + addr_len = read_eeprom(nic,0,8) == 0x8129 ? 8 : 6; + for (i = 0; i < 3; i++) + *ap++ = read_eeprom(nic,i + 7,addr_len); + + speed10 = inb(nic->ioaddr + MediaStatus) & MSRSpeed10; + fullduplex = inw(nic->ioaddr + MII_BMCR) & BMCRDuplex; + printf("ioaddr %#hX, irq %d, addr %! %sMbps %s-duplex\n", nic->ioaddr, + nic->irqno, nic->node_addr, speed10 ? "10" : "100", + fullduplex ? "full" : "half"); + + rtl_reset(nic); + + if (inb(nic->ioaddr + MediaStatus) & MSRLinkFail) { + printf("Cable not connected or other link failure\n"); + return(0); + } + + dev->disable = rtl_disable; + nic->poll = rtl_poll; + nic->transmit = rtl_transmit; + nic->irq = rtl_irq; + + return 1; +} + +/* Serial EEPROM section. */ + +/* EEPROM_Ctrl bits. */ +#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */ +#define EE_CS 0x08 /* EEPROM chip select. */ +#define EE_DATA_WRITE 0x02 /* EEPROM chip data in. */ +#define EE_WRITE_0 0x00 +#define EE_WRITE_1 0x02 +#define EE_DATA_READ 0x01 /* EEPROM chip data out. */ +#define EE_ENB (0x80 | EE_CS) + +/* + Delay between EEPROM clock transitions. + No extra delay is needed with 33Mhz PCI, but 66Mhz may change this. +*/ + +#define eeprom_delay() inl(ee_addr) + +/* The EEPROM commands include the alway-set leading bit. */ +#define EE_WRITE_CMD (5) +#define EE_READ_CMD (6) +#define EE_ERASE_CMD (7) + +static int read_eeprom(struct nic *nic, int location, int addr_len) +{ + int i; + unsigned int retval = 0; + long ee_addr = nic->ioaddr + Cfg9346; + int read_cmd = location | (EE_READ_CMD << addr_len); + + outb(EE_ENB & ~EE_CS, ee_addr); + outb(EE_ENB, ee_addr); + eeprom_delay(); + + /* Shift the read command bits out. */ + for (i = 4 + addr_len; i >= 0; i--) { + int dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + outb(EE_ENB | dataval, ee_addr); + eeprom_delay(); + outb(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); + eeprom_delay(); + } + outb(EE_ENB, ee_addr); + eeprom_delay(); + + for (i = 16; i > 0; i--) { + outb(EE_ENB | EE_SHIFT_CLK, ee_addr); + eeprom_delay(); + retval = (retval << 1) | ((inb(ee_addr) & EE_DATA_READ) ? 1 : 0); + outb(EE_ENB, ee_addr); + eeprom_delay(); + } + + /* Terminate the EEPROM access. */ + outb(~EE_CS, ee_addr); + eeprom_delay(); + return retval; +} + +static const unsigned int rtl8139_rx_config = + (RX_BUF_LEN_IDX << 11) | + (RX_FIFO_THRESH << 13) | + (RX_DMA_BURST << 8); + +static void set_rx_mode(struct nic *nic) { + unsigned int mc_filter[2]; + int rx_mode; + /* !IFF_PROMISC */ + rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; + mc_filter[1] = mc_filter[0] = 0xffffffff; + + outl(rtl8139_rx_config | rx_mode, nic->ioaddr + RxConfig); + + outl(mc_filter[0], nic->ioaddr + MAR0 + 0); + outl(mc_filter[1], nic->ioaddr + MAR0 + 4); +} + +static void rtl_reset(struct nic* nic) +{ + int i; + + outb(CmdReset, nic->ioaddr + ChipCmd); + + cur_rx = 0; + cur_tx = 0; + + /* Give the chip 10ms to finish the reset. */ + load_timer2(10*TICKS_PER_MS); + while ((inb(nic->ioaddr + ChipCmd) & CmdReset) != 0 && + timer2_running()) + /* wait */; + + for (i = 0; i < ETH_ALEN; i++) + outb(nic->node_addr[i], nic->ioaddr + MAC0 + i); + + /* Must enable Tx/Rx before setting transfer thresholds! */ + outb(CmdRxEnb | CmdTxEnb, nic->ioaddr + ChipCmd); + outl((RX_FIFO_THRESH<<13) | (RX_BUF_LEN_IDX<<11) | (RX_DMA_BURST<<8), + nic->ioaddr + RxConfig); /* accept no frames yet! */ + outl((TX_DMA_BURST<<8)|0x03000000, nic->ioaddr + TxConfig); + + /* The Linux driver changes Config1 here to use a different LED pattern + * for half duplex or full/autodetect duplex (for full/autodetect, the + * outputs are TX/RX, Link10/100, FULL, while for half duplex it uses + * TX/RX, Link100, Link10). This is messy, because it doesn't match + * the inscription on the mounting bracket. It should not be changed + * from the configuration EEPROM default, because the card manufacturer + * should have set that to match the card. */ + +#ifdef DEBUG_RX + printf("rx ring address is %X\n",(unsigned long)rx_ring); +#endif + outl((unsigned long)virt_to_bus(rx_ring), nic->ioaddr + RxBuf); + + + + /* If we add multicast support, the MAR0 register would have to be + * initialized to 0xffffffffffffffff (two 32 bit accesses). Etherboot + * only needs broadcast (for ARP/RARP/BOOTP/DHCP) and unicast. */ + + outb(CmdRxEnb | CmdTxEnb, nic->ioaddr + ChipCmd); + + outl(rtl8139_rx_config, nic->ioaddr + RxConfig); + + /* Start the chip's Tx and Rx process. */ + outl(0, nic->ioaddr + RxMissed); + + /* set_rx_mode */ + set_rx_mode(nic); + + /* Disable all known interrupts by setting the interrupt mask. */ + outw(0, nic->ioaddr + IntrMask); +} + +static void rtl_transmit(struct nic *nic, const char *destaddr, + unsigned int type, unsigned int len, const char *data) +{ + unsigned int status, to, nstype; + unsigned long txstatus; + + /* nstype assignment moved up here to avoid gcc 3.0.3 compiler bug */ + nstype = htons(type); + memcpy(tx_buffer, destaddr, ETH_ALEN); + memcpy(tx_buffer + ETH_ALEN, nic->node_addr, ETH_ALEN); + memcpy(tx_buffer + 2 * ETH_ALEN, &nstype, 2); + memcpy(tx_buffer + ETH_HLEN, data, len); + + len += ETH_HLEN; +#ifdef DEBUG_TX + printf("sending %d bytes ethtype %hX\n", len, type); +#endif + + /* Note: RTL8139 doesn't auto-pad, send minimum payload (another 4 + * bytes are sent automatically for the FCS, totalling to 64 bytes). */ + while (len < ETH_ZLEN) { + tx_buffer[len++] = '\0'; + } + + outl((unsigned long)virt_to_bus(tx_buffer), nic->ioaddr + TxAddr0 + cur_tx*4); + outl(((TX_FIFO_THRESH<<11) & 0x003f0000) | len, + nic->ioaddr + TxStatus0 + cur_tx*4); + + to = currticks() + RTL_TIMEOUT; + + do { + status = inw(nic->ioaddr + IntrStatus); + /* Only acknlowledge interrupt sources we can properly handle + * here - the RxOverflow/RxFIFOOver MUST be handled in the + * rtl_poll() function. */ + outw(status & (TxOK | TxErr | PCIErr), nic->ioaddr + IntrStatus); + if ((status & (TxOK | TxErr | PCIErr)) != 0) break; + } while (currticks() < to); + + txstatus = inl(nic->ioaddr+ TxStatus0 + cur_tx*4); + + if (status & TxOK) { + cur_tx = (cur_tx + 1) % NUM_TX_DESC; +#ifdef DEBUG_TX + printf("tx done (%d ticks), status %hX txstatus %X\n", + to-currticks(), status, txstatus); +#endif + } else { +#ifdef DEBUG_TX + printf("tx timeout/error (%d ticks), status %hX txstatus %X\n", + currticks()-to, status, txstatus); +#endif + rtl_reset(nic); + } +} + +static int rtl_poll(struct nic *nic, int retrieve) +{ + unsigned int status; + unsigned int ring_offs; + unsigned int rx_size, rx_status; + + if (inb(nic->ioaddr + ChipCmd) & RxBufEmpty) { + return 0; + } + + /* There is a packet ready */ + if ( ! retrieve ) return 1; + + status = inw(nic->ioaddr + IntrStatus); + /* See below for the rest of the interrupt acknowledges. */ + outw(status & ~(RxFIFOOver | RxOverflow | RxOK), nic->ioaddr + IntrStatus); + +#ifdef DEBUG_RX + printf("rtl_poll: int %hX ", status); +#endif + + ring_offs = cur_rx % RX_BUF_LEN; + rx_status = *(unsigned int*)(rx_ring + ring_offs); + rx_size = rx_status >> 16; + rx_status &= 0xffff; + + if ((rx_status & (RxBadSymbol|RxRunt|RxTooLong|RxCRCErr|RxBadAlign)) || + (rx_size < ETH_ZLEN) || (rx_size > ETH_FRAME_LEN + 4)) { + printf("rx error %hX\n", rx_status); + rtl_reset(nic); /* this clears all interrupts still pending */ + return 0; + } + + /* Received a good packet */ + nic->packetlen = rx_size - 4; /* no one cares about the FCS */ + if (ring_offs+4+rx_size-4 > RX_BUF_LEN) { + int semi_count = RX_BUF_LEN - ring_offs - 4; + + memcpy(nic->packet, rx_ring + ring_offs + 4, semi_count); + memcpy(nic->packet+semi_count, rx_ring, rx_size-4-semi_count); +#ifdef DEBUG_RX + printf("rx packet %d+%d bytes", semi_count,rx_size-4-semi_count); +#endif + } else { + memcpy(nic->packet, rx_ring + ring_offs + 4, nic->packetlen); +#ifdef DEBUG_RX + printf("rx packet %d bytes", rx_size-4); +#endif + } +#ifdef DEBUG_RX + printf(" at %X type %hhX%hhX rxstatus %hX\n", + (unsigned long)(rx_ring+ring_offs+4), + nic->packet[12], nic->packet[13], rx_status); +#endif + cur_rx = (cur_rx + rx_size + 4 + 3) & ~3; + outw(cur_rx - 16, nic->ioaddr + RxBufPtr); + /* See RTL8139 Programming Guide V0.1 for the official handling of + * Rx overflow situations. The document itself contains basically no + * usable information, except for a few exception handling rules. */ + outw(status & (RxFIFOOver | RxOverflow | RxOK), nic->ioaddr + IntrStatus); + return 1; +} + +static void rtl_irq(struct nic *nic, irq_action_t action) +{ + unsigned int mask; + /* Bit of a guess as to which interrupts we should allow */ + unsigned int interested = ROK | RER | RXOVW | FOVW | SERR; + + switch ( action ) { + case DISABLE : + case ENABLE : + mask = inw(nic->ioaddr + IntrMask); + mask = mask & ~interested; + if ( action == ENABLE ) mask = mask | interested; + outw(mask, nic->ioaddr + IntrMask); + break; + case FORCE : + /* Apparently writing a 1 to this read-only bit of a + * read-only and otherwise unrelated register will + * force an interrupt. If you ever want to see how + * not to write a datasheet, read the one for the + * RTL8139... + */ + outb(EROK, nic->ioaddr + RxEarlyStatus); + break; + } +} + +static void rtl_disable(struct dev *dev) +{ + struct nic *nic = (struct nic *)dev; + /* merge reset and disable */ + rtl_reset(nic); + + /* reset the chip */ + outb(CmdReset, nic->ioaddr + ChipCmd); + + /* 10 ms timeout */ + load_timer2(10*TICKS_PER_MS); + while ((inb(nic->ioaddr + ChipCmd) & CmdReset) != 0 && timer2_running()) + /* wait */; +} + +static struct pci_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"), +}; + +static struct pci_driver rtl8139_driver __pci_driver = { + .type = NIC_DRIVER, + .name = "RTL8139", + .probe = rtl8139_probe, + .ids = rtl8139_nics, + .id_count = sizeof(rtl8139_nics)/sizeof(rtl8139_nics[0]), + .class = 0, +}; diff --git a/src/drivers/net/sis900.c b/src/drivers/net/sis900.c new file mode 100644 index 00000000..c4c873da --- /dev/null +++ b/src/drivers/net/sis900.c @@ -0,0 +1,1271 @@ +/* -*- 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@thinguin.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 "nic.h" +#include "pci.h" +#include "timer.h" + +#include "sis900.h" + +/* Globals */ + +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; + +static BufferDesc txd; +static BufferDesc rxd[NUM_RX_DESC]; +static unsigned char txb[TX_BUF_SIZE]; +static unsigned char rxb[NUM_RX_DESC * RX_BUF_SIZE]; + +#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}, + {"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; + + +// PCI to ISA bridge for SIS640E access +static struct pci_id pci_isa_bridge_list[] = { + { 0x1039, 0x0008, + "SIS 85C503/5513 PCI to ISA bridge"}, +}; + +static struct pci_driver sis_bridge_driver __pci_driver = { + .type = BRIDGE_DRIVER, + .name = "", + .probe = 0, + .ids = pci_isa_bridge_list, + .id_count = sizeof(pci_isa_bridge_list)/sizeof(pci_isa_bridge_list[0]), + .class = 0, +}; + +/* Function Prototypes */ + +static int sis900_probe(struct dev *dev, 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 dev *dev); + +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) +{ + u8 reg; + int i; + struct pci_device p[1]; + + /* find PCI to ISA bridge */ + memset(p, 0, sizeof(p)); + do { + find_pci(BRIDGE_DRIVER, p); + } while(p->driver && p->driver != &sis_bridge_driver); + + /* error on failure */ + if (!p->driver) + return 0; + + pcibios_read_config_byte(p->bus,p->devfn, 0x48, ®); + pcibios_write_config_byte(p->bus,p->devfn, 0x48, reg | 0x40); + + for (i = 0; i < ETH_ALEN; i++) + { + outb(0x09 + i, 0x70); + ((u8 *)(nic->node_addr))[i] = inb(0x71); + } + pcibios_write_config_byte(p->bus,p->devfn, 0x48, reg & ~0x40); + + return 1; +} + +/** + * 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 dev *dev, struct pci_device *pci) +{ + struct nic *nic = (struct nic *)dev; + 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 & ~3; + ioaddr = pci->ioaddr & ~3; + vendor = pci->vendor; + dev_id = pci->dev_id; + + /* wakeup chip */ + pcibios_write_config_dword(pci->bus, pci->devfn, 0x40, 0x00000000); + + adjust_pci_device(pci); + + /* get MAC address */ + ret = 0; + pcibios_read_config_byte(pci->bus,pci->devfn, 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); + + printf("\nsis900_probe: MAC addr %! at ioaddr %#hX\n", + nic->node_addr, ioaddr); + printf("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); + + dev->disable = sis900_disable; + nic->poll = sis900_poll; + nic->transmit = sis900_transmit; + nic->irq = sis900_irq; + + 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<= 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<= 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]=%X\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: %X\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]=%X link=%X cmdsts=%X bufptr=%X\n", + i, &rxd[i], rxd[i].link, rxd[i].cmdsts, 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: %X\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 */ + while (i++ < 2) + status = sis900_mdio_read(phy_addr, MII_STSOUT); + + *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; + 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: %X\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 ((((volatile u32) tx_status=txd.cmdsts) & OWN) && (currticks() < to)) + /* wait */ ; + + if (currticks() >= to) { + printf("sis900_transmit: TX Timeout! Tx status %X.\n", tx_status); + } + + if (tx_status & (ABORT | UNDERRUN | OWCOLL)) { + /* packet unsuccessfully transmited */ + printf("sis900_transmit: Transmit error, Tx status %X.\n", 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, 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, 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", + 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 dev *dev) +{ + struct nic *nic = (struct nic *)dev; + /* merge reset and disable */ + 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 pci_id sis900_nics[] = { +PCI_ROM(0x1039, 0x0900, "sis900", "SIS900"), +PCI_ROM(0x1039, 0x7016, "sis7016", "SIS7016"), +}; + +static struct pci_driver sis900_driver __pci_driver = { + .type = NIC_DRIVER, + .name = "SIS900", + .probe = sis900_probe, + .ids = sis900_nics, + .id_count = sizeof(sis900_nics)/sizeof(sis900_nics[0]), + .class = 0, +}; diff --git a/src/drivers/net/sis900.h b/src/drivers/net/sis900.h new file mode 100644 index 00000000..89aa3aa5 --- /dev/null +++ b/src/drivers/net/sis900.h @@ -0,0 +1,380 @@ +/* -*- 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. */ + +typedef unsigned char u8; +typedef signed char s8; +typedef unsigned short u16; +typedef signed short s16; +typedef unsigned int u32; +typedef signed int s32; + +/* 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/src/drivers/net/sis900.txt b/src/drivers/net/sis900.txt new file mode 100644 index 00000000..822da0cd --- /dev/null +++ b/src/drivers/net/sis900.txt @@ -0,0 +1,91 @@ +How I added the SIS900 card to Etherboot + +Author: Marty Connor (mdc@thinguin.org) + +Date: 25 Febrary 2001 + +Description: + +This file is intended to help people who want to write an Etherboot +driver or port another driver to Etherboot. It is a starting point. +Perhaps someday I may write a more detailed description of writing an +Etherboot driver. This text should help get people started, and +studying sis900.[ch] should help show the basic structure and +techniques involved in writing and Etherboot driver. + +*********************************************************************** + +0. Back up all the files I need to modify: + +cd etherboot-4.7.20/src +cp Makefile Makefile.orig +cp config.c config.c.orig +cp pci.h pci.h.orig +cp NIC NIC.orig +cp cards.h cards.h.orig + +1. Edit src/Makefile to add SIS900FLAGS to defines + +SIS900FLAGS= -DINCLUDE_SIS900 + +2. edit src/pci.h to add PCI signatures for card + +#define PCI_VENDOR_ID_SIS 0x1039 +#define PCI_DEVICE_ID_SIS900 0x0900 +#define PCI_DEVICE_ID_SIS7016 0x7016 + +3. Edit src/config.c to add the card to the card probe list + +#if defined(INCLUDE_NS8390) || defined(INCLUDE_EEPRO100) || + defined(INCLUDE_LANCE) || defined(INCLUDE_EPIC100) || + defined(INCLUDE_TULIP) || defined(INCLUDE_OTULIP) || + defined(INCLUDE_3C90X) || defined(INCLUDE_3C595) || + defined(INCLUDE_RTL8139) || defined(INCLUDE_VIA_RHINE) || + defined(INCLUDE_SIS900) || defined(INCLUDE_W89C840) + +... and ... + +#ifdef INCLUDE_SIS900 + { PCI_VENDOR_ID_SIS, PCI_DEVICE_ID_SIS900, + "SIS900", 0, 0, 0, 0}, + { PCI_VENDOR_ID_SIS, PCI_DEVICE_ID_SIS7016, + "SIS7016", 0, 0, 0, 0}, +#endif + +... and ... + +#ifdef INCLUDE_SIS900 + { "SIS900", sis900_probe, pci_ioaddrs }, +#endif + +4. Edit NIC to add sis900 and sis7016 to NIC list + +# SIS 900 and SIS 7016 +sis900 sis900 0x1039,0x0900 +sis7016 sis900 0x1039,0x7016 + +5. Edit cards.h to add sis900 probe routine declaration + +#ifdef INCLUDE_SIS900 +extern struct nic *sis900_probe(struct nic *, unsigned short * + PCI_ARG(struct pci_device *)); +#endif + +*********************************************************************** + +At this point, you can begin creating your driver source file. See +the "Writing an Etherboot Driver" section of the Etherboot +documentation for some hints. See the skel.c file for a starting +point. If there is a Linux driver for the card, you may be able to +use that. Copy and learn from existing Etherboot drivers (this is GPL +/ Open Source software!). + +Join the etherboot-developers and etherboot-users mailing lists +(information is on http://etherboot.sourceforge.net) for information and +assistance. We invite more developers to help improve Etherboot. + +Visit the http://etherboot.sourceforge.net, http://thinguin.org, +http://rom-o-matic.net, and http://ltsp.org sites for information and +assistance. + +Enjoy. diff --git a/src/drivers/net/sk_g16.c b/src/drivers/net/sk_g16.c new file mode 100644 index 00000000..6591c020 --- /dev/null +++ b/src/drivers/net/sk_g16.c @@ -0,0 +1,1189 @@ +/* Uses lance chip, not fixed for relocation */ +#ifdef ALLMULTI +#error multicast support is not yet implemented +#endif +/************************************************************************** +Etherboot - BOOTP/TFTP Bootstrap Program +Schneider & Koch G16 NIC driver for Etherboot +heavily based on SK G16 driver from Linux 2.0.36 +Changes to make it work with Etherboot by Georg Baum +***************************************************************************/ + +/*- + * Copyright (C) 1994 by PJD Weichmann & SWS Bern, Switzerland + * + * This software may be used and distributed according to the terms + * of the GNU Public License, incorporated herein by reference. + * + * Module : sk_g16.c + * + * Version : $Revision$ + * + * Author : Patrick J.D. Weichmann + * + * Date Created : 94/05/26 + * Last Updated : $Date$ + * + * Description : Schneider & Koch G16 Ethernet Device Driver for + * Linux Kernel >= 1.1.22 + * Update History : + * +-*/ + +/* + * The Schneider & Koch (SK) G16 Network device driver is based + * on the 'ni6510' driver from Michael Hipp which can be found at + * ftp://sunsite.unc.edu/pub/Linux/system/Network/drivers/nidrivers.tar.gz + * + * Sources: 1) ni6510.c by M. Hipp + * 2) depca.c by D.C. Davies + * 3) skeleton.c by D. Becker + * 4) Am7990 Local Area Network Controller for Ethernet (LANCE), + * AMD, Pub. #05698, June 1989 + * + * Many Thanks for helping me to get things working to: + * + * A. Cox (A.Cox@swansea.ac.uk) + * M. Hipp (mhipp@student.uni-tuebingen.de) + * R. Bolz (Schneider & Koch, Germany) + * + * See README.sk_g16 for details about limitations and bugs for the + * current version. + * + * To Do: + * - Support of SK_G8 and other SK Network Cards. + * - Autoset memory mapped RAM. Check for free memory and then + * configure RAM correctly. + * - SK_close should really set card in to initial state. + * - Test if IRQ 3 is not switched off. Use autoirq() functionality. + * (as in /drivers/net/skeleton.c) + * - Implement Multicast addressing. At minimum something like + * in depca.c. + * - Redo the statistics part. + * - Try to find out if the board is in 8 Bit or 16 Bit slot. + * If in 8 Bit mode don't use IRQ 11. + * - (Try to make it slightly faster.) + */ + +/* to get some global routines like printf */ +#include "etherboot.h" +/* to get the interface to the body of the program */ +#include "nic.h" +#include "isa.h" + +/* From linux/if_ether.h: */ +#define ETH_ZLEN 60 /* Min. octets in frame sans FCS */ + +#include "sk_g16.h" + +/* + * Schneider & Koch Card Definitions + * ================================= + */ + +#define SK_NAME "SK_G16" + +/* + * SK_G16 Configuration + * -------------------- + */ + +/* + * Abbreviations + * ------------- + * + * RAM - used for the 16KB shared memory + * Boot_ROM, ROM - are used for referencing the BootEPROM + * + * SK_ADDR is a symbolic constant used to configure + * the behaviour of the driver and the SK_G16. + * + * SK_ADDR defines the address where the RAM will be mapped into the real + * host memory. + * valid addresses are from 0xa0000 to 0xfc000 in 16Kbyte steps. + */ + +#define SK_ADDR 0xcc000 + +/* + * In POS3 are bits A14-A19 of the address bus. These bits can be set + * to choose the RAM address. That's why we only can choose the RAM address + * in 16KB steps. + */ + +#define POS_ADDR (rom_addr>>14) /* Do not change this line */ + +/* + * SK_G16 I/O PORT's + IRQ's + Boot_ROM locations + * ---------------------------------------------- + */ + +/* + * As nearly every card has also SK_G16 a specified I/O Port region and + * only a few possible IRQ's. + * In the Installation Guide from Schneider & Koch is listed a possible + * Interrupt IRQ2. IRQ2 is always IRQ9 in boards with two cascaded interrupt + * controllers. So we use in SK_IRQS IRQ9. + */ + +/* Don't touch any of the following #defines. */ + +#define SK_IO_PORTS { 0x100, 0x180, 0x208, 0x220, 0x288, 0x320, 0x328, 0x390, 0 } + +/* + * SK_G16 POS REGISTERS + * -------------------- + */ + +/* + * SK_G16 has a Programmable Option Select (POS) Register. + * The POS is composed of 8 separate registers (POS0-7) which + * are I/O mapped on an address set by the W1 switch. + * + */ + +#define SK_POS_SIZE 8 /* 8 I/O Ports are used by SK_G16 */ + +#define SK_POS0 ioaddr /* Card-ID Low (R) */ +#define SK_POS1 ioaddr+1 /* Card-ID High (R) */ +#define SK_POS2 ioaddr+2 /* Card-Enable, Boot-ROM Disable (RW) */ +#define SK_POS3 ioaddr+3 /* Base address of RAM */ +#define SK_POS4 ioaddr+4 /* IRQ */ + +/* POS5 - POS7 are unused */ + +/* + * SK_G16 MAC PREFIX + * ----------------- + */ + +/* + * Scheider & Koch manufacturer code (00:00:a5). + * This must be checked, that we are sure it is a SK card. + */ + +#define SK_MAC0 0x00 +#define SK_MAC1 0x00 +#define SK_MAC2 0x5a + +/* + * SK_G16 ID + * --------- + */ + +/* + * If POS0,POS1 contain the following ID, then we know + * at which I/O Port Address we are. + */ + +#define SK_IDLOW 0xfd +#define SK_IDHIGH 0x6a + + +/* + * LANCE POS Bit definitions + * ------------------------- + */ + +#define SK_ROM_RAM_ON (POS2_CARD) +#define SK_ROM_RAM_OFF (POS2_EPROM) +#define SK_ROM_ON (inb(SK_POS2) & POS2_CARD) +#define SK_ROM_OFF (inb(SK_POS2) | POS2_EPROM) +#define SK_RAM_ON (inb(SK_POS2) | POS2_CARD) +#define SK_RAM_OFF (inb(SK_POS2) & POS2_EPROM) + +#define POS2_CARD 0x0001 /* 1 = SK_G16 on 0 = off */ +#define POS2_EPROM 0x0002 /* 1 = Boot EPROM off 0 = on */ + +/* + * SK_G16 Memory mapped Registers + * ------------------------------ + * + */ + +#define SK_IOREG (board->ioreg) /* LANCE data registers. */ +#define SK_PORT (board->port) /* Control, Status register */ +#define SK_IOCOM (board->iocom) /* I/O Command */ + +/* + * SK_G16 Status/Control Register bits + * ----------------------------------- + * + * (C) Controlreg (S) Statusreg + */ + +/* + * Register transfer: 0 = no transfer + * 1 = transferring data between LANCE and I/O reg + */ +#define SK_IORUN 0x20 + +/* + * LANCE interrupt: 0 = LANCE interrupt occurred + * 1 = no LANCE interrupt occurred + */ +#define SK_IRQ 0x10 + +#define SK_RESET 0x08 /* Reset SK_CARD: 0 = RESET 1 = normal */ +#define SK_RW 0x02 /* 0 = write to 1 = read from */ +#define SK_ADR 0x01 /* 0 = REG DataPort 1 = RAP Reg addr port */ + + +#define SK_RREG SK_RW /* Transferdirection to read from lance */ +#define SK_WREG 0 /* Transferdirection to write to lance */ +#define SK_RAP SK_ADR /* Destination Register RAP */ +#define SK_RDATA 0 /* Destination Register REG DataPort */ + +/* + * SK_G16 I/O Command + * ------------------ + */ + +/* + * Any bitcombination sets the internal I/O bit (transfer will start) + * when written to I/O Command + */ + +#define SK_DOIO 0x80 /* Do Transfer */ + +/* + * LANCE RAP (Register Address Port). + * --------------------------------- + */ + +/* + * The LANCE internal registers are selected through the RAP. + * The Registers are: + * + * CSR0 - Status and Control flags + * CSR1 - Low order bits of initialize block (bits 15:00) + * CSR2 - High order bits of initialize block (bits 07:00, 15:08 are reserved) + * CSR3 - Allows redefinition of the Bus Master Interface. + * This register must be set to 0x0002, which means BSWAP = 0, + * ACON = 1, BCON = 0; + * + */ + +#define CSR0 0x00 +#define CSR1 0x01 +#define CSR2 0x02 +#define CSR3 0x03 + +/* + * General Definitions + * =================== + */ + +/* + * Set the number of Tx and Rx buffers, using Log_2(# buffers). + * We have 16KB RAM which can be accessed by the LANCE. In the + * memory are not only the buffers but also the ring descriptors and + * the initialize block. + * Don't change anything unless you really know what you do. + */ + +#define LC_LOG_TX_BUFFERS 1 /* (2 == 2^^1) 2 Transmit buffers */ +#define LC_LOG_RX_BUFFERS 2 /* (8 == 2^^3) 8 Receive buffers */ + +/* Descriptor ring sizes */ + +#define TMDNUM (1 << (LC_LOG_TX_BUFFERS)) /* 2 Transmit descriptor rings */ +#define RMDNUM (1 << (LC_LOG_RX_BUFFERS)) /* 8 Receive Buffers */ + +/* Define Mask for setting RMD, TMD length in the LANCE init_block */ + +#define TMDNUMMASK (LC_LOG_TX_BUFFERS << 29) +#define RMDNUMMASK (LC_LOG_RX_BUFFERS << 29) + +/* + * Data Buffer size is set to maximum packet length. + */ + +#define PKT_BUF_SZ 1518 + +/* + * The number of low I/O ports used by the ethercard. + */ + +#define ETHERCARD_TOTAL_SIZE SK_POS_SIZE + +/* + * Portreserve is there to mark the Card I/O Port region as used. + * Check_region is to check if the region at ioaddr with the size "size" + * is free or not. + * Snarf_region allocates the I/O Port region. + */ + +#ifndef HAVE_PORTRESERVE + +#define check_region(ioaddr1, size) 0 +#define request_region(ioaddr1, size,name) do ; while (0) + +#endif + +/* + * SK_DEBUG + * + * Here you can choose what level of debugging wanted. + * + * If SK_DEBUG and SK_DEBUG2 are undefined, then only the + * necessary messages will be printed. + * + * If SK_DEBUG is defined, there will be many debugging prints + * which can help to find some mistakes in configuration or even + * in the driver code. + * + * If SK_DEBUG2 is defined, many many messages will be printed + * which normally you don't need. I used this to check the interrupt + * routine. + * + * (If you define only SK_DEBUG2 then only the messages for + * checking interrupts will be printed!) + * + * Normal way of live is: + * + * For the whole thing get going let both symbolic constants + * undefined. If you face any problems and you know what's going + * on (you know something about the card and you can interpret some + * hex LANCE register output) then define SK_DEBUG + * + */ + +#undef SK_DEBUG /* debugging */ +#undef SK_DEBUG2 /* debugging with more verbose report */ + +#ifdef SK_DEBUG +#define PRINTF(x) printf x +#else +#define PRINTF(x) /**/ +#endif + +#ifdef SK_DEBUG2 +#define PRINTF2(x) printf x +#else +#define PRINTF2(x) /**/ +#endif + +/* + * SK_G16 RAM + * + * The components are memory mapped and can be set in a region from + * 0x00000 through 0xfc000 in 16KB steps. + * + * The Network components are: dual ported RAM, Prom, I/O Reg, Status-, + * Controlregister and I/O Command. + * + * dual ported RAM: This is the only memory region which the LANCE chip + * has access to. From the Lance it is addressed from 0x0000 to + * 0x3fbf. The host accesses it normally. + * + * PROM: The PROM obtains the ETHERNET-MAC-Address. It is realised as a + * 8-Bit PROM, this means only the 16 even addresses are used of the + * 32 Byte Address region. Access to a odd address results in invalid + * data. + * + * LANCE I/O Reg: The I/O Reg is build of 4 single Registers, Low-Byte Write, + * Hi-Byte Write, Low-Byte Read, Hi-Byte Read. + * Transfer from or to the LANCE is always in 16Bit so Low and High + * registers are always relevant. + * + * The Data from the Readregister is not the data in the Writeregister!! + * + * Port: Status- and Controlregister. + * Two different registers which share the same address, Status is + * read-only, Control is write-only. + * + * I/O Command: + * Any bitcombination written in here starts the transmission between + * Host and LANCE. + */ + +typedef struct +{ + unsigned char ram[0x3fc0]; /* 16KB dual ported ram */ + unsigned char rom[0x0020]; /* 32Byte PROM containing 6Byte MAC */ + unsigned char res1[0x0010]; /* reserved */ + unsigned volatile short ioreg;/* LANCE I/O Register */ + unsigned volatile char port; /* Statusregister and Controlregister */ + unsigned char iocom; /* I/O Command Register */ +} SK_RAM; + +/* struct */ + +/* + * This is the structure for the dual ported ram. We + * have exactly 16 320 Bytes. In here there must be: + * + * - Initialize Block (starting at a word boundary) + * - Receive and Transmit Descriptor Rings (quadword boundary) + * - Data Buffers (arbitrary boundary) + * + * This is because LANCE has on SK_G16 only access to the dual ported + * RAM and nowhere else. + */ + +struct SK_ram +{ + struct init_block ib; + struct tmd tmde[TMDNUM]; + struct rmd rmde[RMDNUM]; + char tmdbuf[TMDNUM][PKT_BUF_SZ]; + char rmdbuf[RMDNUM][PKT_BUF_SZ]; +}; + +/* + * Structure where all necessary information is for ring buffer + * management and statistics. + */ + +struct priv +{ + struct SK_ram *ram; /* dual ported ram structure */ + struct rmd *rmdhead; /* start of receive ring descriptors */ + struct tmd *tmdhead; /* start of transmit ring descriptors */ + int rmdnum; /* actual used ring descriptor */ + int tmdnum; /* actual transmit descriptor for transmitting data */ + int tmdlast; /* last sent descriptor used for error handling, etc */ + void *rmdbufs[RMDNUM]; /* pointer to the receive buffers */ + void *tmdbufs[TMDNUM]; /* pointer to the transmit buffers */ +}; + +/* global variable declaration */ + +/* static variables */ + +static SK_RAM *board; /* pointer to our memory mapped board components */ +static unsigned short ioaddr; /* base io address */ +static struct priv p_data; + +/* Macros */ + + +/* Function Prototypes */ + +/* + * Device Driver functions + * ----------------------- + * See for short explanation of each function its definitions header. + */ + +static int SK_probe1(struct nic *nic, short ioaddr1); + +static int SK_poll(struct nic *nic, int retrieve); +static void SK_transmit( +struct nic *nic, +const char *d, /* Destination */ +unsigned int t, /* Type */ +unsigned int s, /* size */ +const char *p); /* Packet */ +static void SK_disable(struct dev *dev); +static int SK_probe(struct dev *dev, unsigned short *probe_addrs); + +/* + * LANCE Functions + * --------------- + */ + +static int SK_lance_init(struct nic *nic, unsigned short mode); +static void SK_reset_board(void); +static void SK_set_RAP(int reg_number); +static int SK_read_reg(int reg_number); +static int SK_rread_reg(void); +static void SK_write_reg(int reg_number, int value); + +/* + * Debugging functions + * ------------------- + */ + +#ifdef SK_DEBUG +static void SK_print_pos(struct nic *nic, char *text); +static void SK_print_ram(struct nic *nic); +#endif + + +/************************************************************************** +POLL - Wait for a frame +***************************************************************************/ +static int SK_poll(struct nic *nic, int retrieve) +{ + /* return true if there's an ethernet packet ready to read */ + struct priv *p; /* SK_G16 private structure */ + struct rmd *rmdp; + int csr0, rmdstat, packet_there; + PRINTF2(("## %s: At beginning of SK_poll(). CSR0: %#hX\n", + SK_NAME, SK_read_reg(CSR0))); + + p = nic->priv_data; + csr0 = SK_read_reg(CSR0); /* store register for checking */ + + rmdp = p->rmdhead + p->rmdnum; + packet_there = 0; + + if ( !(rmdp->u.s.status & RX_OWN) && !retrieve ) return 1; + + /* + * Acknowledge all of the current interrupt sources, disable + * Interrupts (INEA = 0) + */ + + SK_write_reg(CSR0, csr0 & CSR0_CLRALL); + + if (csr0 & CSR0_ERR) /* LANCE Error */ + { + printf("%s: error: %#hX", SK_NAME, csr0); + + if (csr0 & CSR0_MISS) /* No place to store packet ? */ + { + printf(", Packet dropped."); + } + putchar('\n'); + } + + /* As long as we own the next entry, check status and send + * it up to higher layer + */ + + while (!( (rmdstat = rmdp->u.s.status) & RX_OWN)) + { + /* + * Start and end of packet must be set, because we use + * the ethernet maximum packet length (1518) as buffer size. + * + * Because our buffers are at maximum OFLO and BUFF errors are + * not to be concerned (see Data sheet) + */ + + if ((rmdstat & (RX_STP | RX_ENP)) != (RX_STP | RX_ENP)) + { + /* Start of a frame > 1518 Bytes ? */ + + if (rmdstat & RX_STP) + { + printf("%s: packet too long\n", SK_NAME); + } + + /* + * All other packets will be ignored until a new frame with + * start (RX_STP) set follows. + * + * What we do is just give descriptor free for new incoming + * packets. + */ + + rmdp->u.s.status = RX_OWN; /* Relinquish ownership to LANCE */ + + } + else if (rmdstat & RX_ERR) /* Receive Error ? */ + { + printf("%s: RX error: %#hX\n", SK_NAME, (int) rmdstat); + rmdp->u.s.status = RX_OWN; /* Relinquish ownership to LANCE */ + } + else /* We have a packet which can be queued for the upper layers */ + { + + int len = (rmdp->mlen & 0x0fff); /* extract message length from receive buffer */ + + /* + * Copy data out of our receive descriptor into nic->packet. + * + * (rmdp->u.buffer & 0x00ffffff) -> get address of buffer and + * ignore status fields) + */ + + memcpy(nic->packet, (unsigned char *) (rmdp->u.buffer & 0x00ffffff), nic->packetlen = len); + packet_there = 1; + + + /* + * Packet is queued and marked for processing so we + * free our descriptor + */ + + rmdp->u.s.status = RX_OWN; + + p->rmdnum++; + p->rmdnum %= RMDNUM; + + rmdp = p->rmdhead + p->rmdnum; + } + } + SK_write_reg(CSR0, CSR0_INEA); /* Enable Interrupts */ + return (packet_there); +} + +/************************************************************************** +TRANSMIT - Transmit a frame +***************************************************************************/ +static void SK_transmit( +struct nic *nic, +const char *d, /* Destination */ +unsigned int t, /* Type */ +unsigned int s, /* size */ +const char *pack) /* Packet */ +{ + /* send the packet to destination */ + struct priv *p; /* SK_G16 private structure */ + struct tmd *tmdp; + short len; + int csr0, tmdstat; + + PRINTF2(("## %s: At beginning of SK_transmit(). CSR0: %#hX\n", + SK_NAME, SK_read_reg(CSR0))); + p = nic->priv_data; + tmdp = p->tmdhead + p->tmdnum; /* Which descriptor for transmitting */ + + /* Copy data into dual ported ram */ + + memcpy(&p->ram->tmdbuf[p->tmdnum][0], d, ETH_ALEN); /* dst */ + memcpy(&p->ram->tmdbuf[p->tmdnum][ETH_ALEN], nic->node_addr, ETH_ALEN); /* src */ + p->ram->tmdbuf[p->tmdnum][ETH_ALEN + ETH_ALEN] = t >> 8; /* type */ + p->ram->tmdbuf[p->tmdnum][ETH_ALEN + ETH_ALEN + 1] = t; /* type */ + memcpy(&p->ram->tmdbuf[p->tmdnum][ETH_HLEN], pack, s); + s += ETH_HLEN; + while (s < ETH_ZLEN) /* pad to min length */ + p->ram->tmdbuf[p->tmdnum][s++] = 0; + p->ram->tmde[p->tmdnum].status2 = 0x0; + + /* Evaluate Packet length */ + len = ETH_ZLEN < s ? s : ETH_ZLEN; + + /* Fill in Transmit Message Descriptor */ + + tmdp->blen = -len; /* set length to transmit */ + + /* + * Packet start and end is always set because we use the maximum + * packet length as buffer length. + * Relinquish ownership to LANCE + */ + + tmdp->u.s.status = TX_OWN | TX_STP | TX_ENP; + + /* Start Demand Transmission */ + SK_write_reg(CSR0, CSR0_TDMD | CSR0_INEA); + + csr0 = SK_read_reg(CSR0); /* store register for checking */ + + /* + * Acknowledge all of the current interrupt sources, disable + * Interrupts (INEA = 0) + */ + + SK_write_reg(CSR0, csr0 & CSR0_CLRALL); + + if (csr0 & CSR0_ERR) /* LANCE Error */ + { + printf("%s: error: %#hX", SK_NAME, csr0); + + if (csr0 & CSR0_MISS) /* No place to store packet ? */ + { + printf(", Packet dropped."); + } + putchar('\n'); + } + + + /* Set next buffer */ + p->tmdlast++; + p->tmdlast &= TMDNUM-1; + + tmdstat = tmdp->u.s.status & 0xff00; /* filter out status bits 15:08 */ + + /* + * We check status of transmitted packet. + * see LANCE data-sheet for error explanation + */ + if (tmdstat & TX_ERR) /* Error occurred */ + { + printf("%s: TX error: %#hX %#hX\n", SK_NAME, (int) tmdstat, + (int) tmdp->status2); + + if (tmdp->status2 & TX_TDR) /* TDR problems? */ + { + printf("%s: tdr-problems \n", SK_NAME); + } + + if (tmdp->status2 & TX_UFLO) /* Underflow error ? */ + { + /* + * If UFLO error occurs it will turn transmitter of. + * So we must reinit LANCE + */ + + SK_lance_init(nic, MODE_NORMAL); + } + + tmdp->status2 = 0; /* Clear error flags */ + } + + SK_write_reg(CSR0, CSR0_INEA); /* Enable Interrupts */ + + /* Set pointer to next transmit buffer */ + p->tmdnum++; + p->tmdnum &= TMDNUM-1; + +} + +/************************************************************************** +DISABLE - Turn off ethernet interface +***************************************************************************/ +static void SK_disable(struct dev *dev) +{ + struct nic *nic = (struct nic *)dev; + + /* put the card in its initial state */ + SK_lance_init(nic, MODE_NORMAL); /* reset and disable merge */ + + PRINTF(("## %s: At beginning of SK_disable(). CSR0: %#hX\n", + SK_NAME, SK_read_reg(CSR0))); + PRINTF(("%s: Shutting %s down CSR0 %#hX\n", SK_NAME, SK_NAME, + (int) SK_read_reg(CSR0))); + + SK_write_reg(CSR0, CSR0_STOP); /* STOP the LANCE */ +} + +/************************************************************************** +IRQ - Enable, Disable, or Force interrupts +***************************************************************************/ +static void SK_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 +***************************************************************************/ +static int SK_probe(struct dev *dev, unsigned short *probe_addrs) +{ + struct nic *nic = (struct nic *)dev; + unsigned short *p; + static unsigned short io_addrs[] = SK_IO_PORTS; + /* if probe_addrs is 0, then routine can use a hardwired default */ + nic->priv_data = &p_data; + if (probe_addrs == 0) + probe_addrs = io_addrs; + for (p = probe_addrs; (ioaddr = *p) != 0; ++p) + { + long offset1, offset0 = inb(ioaddr); + if ((offset0 == SK_IDLOW) && + ((offset1 = inb(ioaddr + 1)) == SK_IDHIGH)) + if (SK_probe1(nic, ioaddr) >= 0) + break; + } + /* if board found */ + if (ioaddr != 0) + { + nic->ioaddr = ioaddr & ~3; + nic->irqno = 0; + /* point to NIC specific routines */ + dev->disable = SK_disable; + nic->poll = SK_poll; + nic->transmit = SK_transmit; + nic->irq = SK_irq; + /* FIXME set dev->devid */ + return 1; + } + /* else */ + { + return 0; + } +} + +int SK_probe1(struct nic *nic, short ioaddr1 __unused) +{ + int i,j; /* Counters */ + unsigned int rom_addr; /* used to store RAM address used for POS_ADDR */ + + struct priv *p; /* SK_G16 private structure */ + + if (SK_ADDR & 0x3fff || SK_ADDR < 0xa0000) + { + /* + * Now here we could use a routine which searches for a free + * place in the ram and set SK_ADDR if found. TODO. + */ + printf("%s: SK_ADDR %#hX is not valid. Check configuration.\n", + SK_NAME, SK_ADDR); + return -1; + } + + rom_addr = SK_ADDR; + + outb(SK_ROM_RAM_OFF, SK_POS2); /* Boot_ROM + RAM off */ + outb(POS_ADDR, SK_POS3); /* Set RAM address */ + outb(SK_ROM_RAM_ON, SK_POS2); /* RAM on, BOOT_ROM on */ +#ifdef SK_DEBUG + SK_print_pos(nic, "POS registers after ROM, RAM config"); +#endif + + board = (SK_RAM *) rom_addr; + PRINTF(("adr[0]: %hX, adr[1]: %hX, adr[2]: %hX\n", + board->rom[0], board->rom[2], board->rom[4])); + + /* Read in station address */ + for (i = 0, j = 0; i < ETH_ALEN; i++, j+=2) + { + *(nic->node_addr+i) = board->rom[j]; + } + + /* Check for manufacturer code */ +#ifdef SK_DEBUG + if (!(*(nic->node_addr+0) == SK_MAC0 && + *(nic->node_addr+1) == SK_MAC1 && + *(nic->node_addr+2) == SK_MAC2) ) + { + PRINTF(("## %s: We did not find SK_G16 at RAM location.\n", + SK_NAME)); + return -1; /* NO SK_G16 found */ + } +#endif + + p = nic->priv_data; + + /* Initialize private structure */ + + p->ram = (struct SK_ram *) rom_addr; /* Set dual ported RAM addr */ + p->tmdhead = &(p->ram)->tmde[0]; /* Set TMD head */ + p->rmdhead = &(p->ram)->rmde[0]; /* Set RMD head */ + + printf("Schneider & Koch G16 at %#hX, mem at %#hX, HW addr: %!\n", + (unsigned int) ioaddr, (unsigned int) p->ram, nic->node_addr); + + /* Initialize buffer pointers */ + + for (i = 0; i < TMDNUM; i++) + { + p->tmdbufs[i] = p->ram->tmdbuf[i]; + } + + for (i = 0; i < RMDNUM; i++) + { + p->rmdbufs[i] = p->ram->rmdbuf[i]; + } + i = 0; + + if (!(i = SK_lance_init(nic, MODE_NORMAL))) /* LANCE init OK? */ + { + +#ifdef SK_DEBUG + /* + * This debug block tries to stop LANCE, + * reinit LANCE with transmitter and receiver disabled, + * then stop again and reinit with NORMAL_MODE + */ + + printf("## %s: After lance init. CSR0: %#hX\n", + SK_NAME, SK_read_reg(CSR0)); + SK_write_reg(CSR0, CSR0_STOP); + printf("## %s: LANCE stopped. CSR0: %#hX\n", + SK_NAME, SK_read_reg(CSR0)); + SK_lance_init(nic, MODE_DTX | MODE_DRX); + printf("## %s: Reinit with DTX + DRX off. CSR0: %#hX\n", + SK_NAME, SK_read_reg(CSR0)); + SK_write_reg(CSR0, CSR0_STOP); + printf("## %s: LANCE stopped. CSR0: %#hX\n", + SK_NAME, SK_read_reg(CSR0)); + SK_lance_init(nic, MODE_NORMAL); + printf("## %s: LANCE back to normal mode. CSR0: %#hX\n", + SK_NAME, SK_read_reg(CSR0)); + SK_print_pos(nic, "POS regs before returning OK"); + +#endif /* SK_DEBUG */ + + } + else /* LANCE init failed */ + { + + PRINTF(("## %s: LANCE init failed: CSR0: %#hX\n", + SK_NAME, SK_read_reg(CSR0))); + return -1; + } + +#ifdef SK_DEBUG + SK_print_pos(nic, "End of SK_probe1"); + SK_print_ram(nic); +#endif + + return 0; /* Initialization done */ + +} /* End of SK_probe1() */ + +static int SK_lance_init(struct nic *nic, unsigned short mode) +{ + int i; + struct priv *p = (struct priv *) nic->priv_data; + struct tmd *tmdp; + struct rmd *rmdp; + + PRINTF(("## %s: At beginning of LANCE init. CSR0: %#hX\n", + SK_NAME, SK_read_reg(CSR0))); + + /* Reset LANCE */ + SK_reset_board(); + + /* Initialize TMD's with start values */ + p->tmdnum = 0; /* First descriptor for transmitting */ + p->tmdlast = 0; /* First descriptor for reading stats */ + + for (i = 0; i < TMDNUM; i++) /* Init all TMD's */ + { + tmdp = p->tmdhead + i; + + tmdp->u.buffer = (unsigned long) p->tmdbufs[i]; /* assign buffer */ + + /* Mark TMD as start and end of packet */ + tmdp->u.s.status = TX_STP | TX_ENP; + } + + + /* Initialize RMD's with start values */ + + p->rmdnum = 0; /* First RMD which will be used */ + + for (i = 0; i < RMDNUM; i++) /* Init all RMD's */ + { + rmdp = p->rmdhead + i; + + + rmdp->u.buffer = (unsigned long) p->rmdbufs[i]; /* assign buffer */ + + /* + * LANCE must be owner at beginning so that he can fill in + * receiving packets, set status and release RMD + */ + + rmdp->u.s.status = RX_OWN; + + rmdp->blen = -PKT_BUF_SZ; /* Buffer Size in a two's complement */ + + rmdp->mlen = 0; /* init message length */ + + } + + /* Fill LANCE Initialize Block */ + + (p->ram)->ib.mode = mode; /* Set operation mode */ + + for (i = 0; i < ETH_ALEN; i++) /* Set physical address */ + { + (p->ram)->ib.paddr[i] = *(nic->node_addr+i); + } + + for (i = 0; i < 8; i++) /* Set multicast, logical address */ + { + (p->ram)->ib.laddr[i] = 0; /* We do not use logical addressing */ + } + + /* Set ring descriptor pointers and set number of descriptors */ + + (p->ram)->ib.rdrp = (int) p->rmdhead | RMDNUMMASK; + (p->ram)->ib.tdrp = (int) p->tmdhead | TMDNUMMASK; + + /* Prepare LANCE Control and Status Registers */ + + SK_write_reg(CSR3, CSR3_ACON); /* Ale Control !!!THIS MUST BE SET!!!! */ + + /* + * LANCE addresses the RAM from 0x0000 to 0x3fbf and has no access to + * PC Memory locations. + * + * In structure SK_ram is defined that the first thing in ram + * is the initialization block. So his address is for LANCE always + * 0x0000 + * + * CSR1 contains low order bits 15:0 of initialization block address + * CSR2 is built of: + * 7:0 High order bits 23:16 of initialization block address + * 15:8 reserved, must be 0 + */ + + /* Set initialization block address (must be on word boundary) */ + SK_write_reg(CSR1, 0); /* Set low order bits 15:0 */ + SK_write_reg(CSR2, 0); /* Set high order bits 23:16 */ + + + PRINTF(("## %s: After setting CSR1-3. CSR0: %#hX\n", + SK_NAME, SK_read_reg(CSR0))); + + /* Initialize LANCE */ + + /* + * INIT = Initialize, when set, causes the LANCE to begin the + * initialization procedure and access the Init Block. + */ + + SK_write_reg(CSR0, CSR0_INIT); + + /* Wait until LANCE finished initialization */ + + SK_set_RAP(CSR0); /* Register Address Pointer to CSR0 */ + + for (i = 0; (i < 100) && !(SK_rread_reg() & CSR0_IDON); i++) + ; /* Wait until init done or go ahead if problems (i>=100) */ + + if (i >= 100) /* Something is wrong ! */ + { + printf("%s: can't init am7990, status: %#hX " + "init_block: %#hX\n", + SK_NAME, (int) SK_read_reg(CSR0), + (unsigned int) &(p->ram)->ib); + +#ifdef SK_DEBUG + SK_print_pos(nic, "LANCE INIT failed"); +#endif + + return -1; /* LANCE init failed */ + } + + PRINTF(("## %s: init done after %d ticks\n", SK_NAME, i)); + + /* Clear Initialize done, enable Interrupts, start LANCE */ + + SK_write_reg(CSR0, CSR0_IDON | CSR0_INEA | CSR0_STRT); + + PRINTF(("## %s: LANCE started. CSR0: %#hX\n", SK_NAME, + SK_read_reg(CSR0))); + + return 0; /* LANCE is up and running */ + +} /* End of SK_lance_init() */ + +/* LANCE access functions + * + * ! CSR1-3 can only be accessed when in CSR0 the STOP bit is set ! + */ + +static void SK_reset_board(void) +{ + int i; + + PRINTF(("## %s: At beginning of SK_reset_board.\n", SK_NAME)); + SK_PORT = 0x00; /* Reset active */ + for (i = 0; i < 10 ; i++) /* Delay min 5ms */ + ; + SK_PORT = SK_RESET; /* Set back to normal operation */ + +} /* End of SK_reset_board() */ + +static void SK_set_RAP(int reg_number) +{ + SK_IOREG = reg_number; + SK_PORT = SK_RESET | SK_RAP | SK_WREG; + SK_IOCOM = SK_DOIO; + + while (SK_PORT & SK_IORUN) + ; +} /* End of SK_set_RAP() */ + +static int SK_read_reg(int reg_number) +{ + SK_set_RAP(reg_number); + + SK_PORT = SK_RESET | SK_RDATA | SK_RREG; + SK_IOCOM = SK_DOIO; + + while (SK_PORT & SK_IORUN) + ; + return (SK_IOREG); + +} /* End of SK_read_reg() */ + +static int SK_rread_reg(void) +{ + SK_PORT = SK_RESET | SK_RDATA | SK_RREG; + + SK_IOCOM = SK_DOIO; + + while (SK_PORT & SK_IORUN) + ; + return (SK_IOREG); + +} /* End of SK_rread_reg() */ + +static void SK_write_reg(int reg_number, int value) +{ + SK_set_RAP(reg_number); + + SK_IOREG = value; + SK_PORT = SK_RESET | SK_RDATA | SK_WREG; + SK_IOCOM = SK_DOIO; + + while (SK_PORT & SK_IORUN) + ; +} /* End of SK_write_reg */ + +/* + * Debugging functions + * ------------------- + */ + +#ifdef SK_DEBUG +static void SK_print_pos(struct nic *nic, char *text) +{ + + unsigned char pos0 = inb(SK_POS0), + pos1 = inb(SK_POS1), + pos2 = inb(SK_POS2), + pos3 = inb(SK_POS3), + pos4 = inb(SK_POS4); + + + printf("## %s: %s.\n" + "## pos0=%#hX pos1=%#hX pos2=%#hX pos3=%#hX pos4=%#hX\n", + SK_NAME, text, pos0, pos1, pos2, (pos3<<14), pos4); + +} /* End of SK_print_pos() */ + +static void SK_print_ram(struct nic *nic) +{ + + int i; + struct priv *p = (struct priv *) nic->priv_data; + + printf("## %s: RAM Details.\n" + "## RAM at %#hX tmdhead: %#hX rmdhead: %#hX initblock: %#hX\n", + SK_NAME, + (unsigned int) p->ram, + (unsigned int) p->tmdhead, + (unsigned int) p->rmdhead, + (unsigned int) &(p->ram)->ib); + + printf("## "); + + for(i = 0; i < TMDNUM; i++) + { + if (!(i % 3)) /* Every third line do a newline */ + { + printf("\n## "); + } + printf("tmdbufs%d: %#hX ", (i+1), (int) p->tmdbufs[i]); + } + printf("## "); + + for(i = 0; i < RMDNUM; i++) + { + if (!(i % 3)) /* Every third line do a newline */ + { + printf("\n## "); + } + printf("rmdbufs%d: %#hX ", (i+1), (int) p->rmdbufs[i]); + } + putchar('\n'); + +} /* End of SK_print_ram() */ +#endif + +static struct isa_driver SK_driver __isa_driver = { + .type = NIC_DRIVER, + .name = "SK_G16", + .probe = SK_probe, + .ioaddrs = 0, +}; diff --git a/src/drivers/net/sk_g16.h b/src/drivers/net/sk_g16.h new file mode 100644 index 00000000..922cb3b6 --- /dev/null +++ b/src/drivers/net/sk_g16.h @@ -0,0 +1,171 @@ +/*- + * + * This software may be used and distributed according to the terms + * of the GNU Public License, incorporated herein by reference. + * + * Module : sk_g16.h + * Version : $Revision$ + * + * Author : M.Hipp (mhipp@student.uni-tuebingen.de) + * changes by : Patrick J.D. Weichmann + * + * Date Created : 94/05/25 + * + * Description : In here are all necessary definitions of + * the am7990 (LANCE) chip used for writing a + * network device driver which uses this chip + * + * $Log$ + * Revision 1.1 2005/03/08 18:53:40 mcb30 + * Initial revision + * + * Revision 1.1 2002/12/12 02:18:20 ebiederm + * Moved network drivers into drivers/net + * +-*/ + +#ifndef SK_G16_H + +#define SK_G16_H + + +/* + * Control and Status Register 0 (CSR0) bit definitions + * + * (R=Readable) (W=Writeable) (S=Set on write) (C-Clear on write) + * + */ + +#define CSR0_ERR 0x8000 /* Error summary (R) */ +#define CSR0_BABL 0x4000 /* Babble transmitter timeout error (RC) */ +#define CSR0_CERR 0x2000 /* Collision Error (RC) */ +#define CSR0_MISS 0x1000 /* Missed packet (RC) */ +#define CSR0_MERR 0x0800 /* Memory Error (RC) */ +#define CSR0_RINT 0x0400 /* Receiver Interrupt (RC) */ +#define CSR0_TINT 0x0200 /* Transmit Interrupt (RC) */ +#define CSR0_IDON 0x0100 /* Initialization Done (RC) */ +#define CSR0_INTR 0x0080 /* Interrupt Flag (R) */ +#define CSR0_INEA 0x0040 /* Interrupt Enable (RW) */ +#define CSR0_RXON 0x0020 /* Receiver on (R) */ +#define CSR0_TXON 0x0010 /* Transmitter on (R) */ +#define CSR0_TDMD 0x0008 /* Transmit Demand (RS) */ +#define CSR0_STOP 0x0004 /* Stop (RS) */ +#define CSR0_STRT 0x0002 /* Start (RS) */ +#define CSR0_INIT 0x0001 /* Initialize (RS) */ + +#define CSR0_CLRALL 0x7f00 /* mask for all clearable bits */ + +/* + * Control and Status Register 3 (CSR3) bit definitions + * + */ + +#define CSR3_BSWAP 0x0004 /* Byte Swap (RW) */ +#define CSR3_ACON 0x0002 /* ALE Control (RW) */ +#define CSR3_BCON 0x0001 /* Byte Control (RW) */ + +/* + * Initialization Block Mode operation Bit Definitions. + */ + +#define MODE_PROM 0x8000 /* Promiscuous Mode */ +#define MODE_INTL 0x0040 /* Internal Loopback */ +#define MODE_DRTY 0x0020 /* Disable Retry */ +#define MODE_COLL 0x0010 /* Force Collision */ +#define MODE_DTCR 0x0008 /* Disable Transmit CRC) */ +#define MODE_LOOP 0x0004 /* Loopback */ +#define MODE_DTX 0x0002 /* Disable the Transmitter */ +#define MODE_DRX 0x0001 /* Disable the Receiver */ + +#define MODE_NORMAL 0x0000 /* Normal operation mode */ + +/* + * Receive message descriptor status bit definitions. + */ + +#define RX_OWN 0x80 /* Owner bit 0 = host, 1 = lance */ +#define RX_ERR 0x40 /* Error Summary */ +#define RX_FRAM 0x20 /* Framing Error */ +#define RX_OFLO 0x10 /* Overflow Error */ +#define RX_CRC 0x08 /* CRC Error */ +#define RX_BUFF 0x04 /* Buffer Error */ +#define RX_STP 0x02 /* Start of Packet */ +#define RX_ENP 0x01 /* End of Packet */ + + +/* + * Transmit message descriptor status bit definitions. + */ + +#define TX_OWN 0x80 /* Owner bit 0 = host, 1 = lance */ +#define TX_ERR 0x40 /* Error Summary */ +#define TX_MORE 0x10 /* More the 1 retry needed to Xmit */ +#define TX_ONE 0x08 /* One retry needed to Xmit */ +#define TX_DEF 0x04 /* Deferred */ +#define TX_STP 0x02 /* Start of Packet */ +#define TX_ENP 0x01 /* End of Packet */ + +/* + * Transmit status (2) (valid if TX_ERR == 1) + */ + +#define TX_BUFF 0x8000 /* Buffering error (no ENP) */ +#define TX_UFLO 0x4000 /* Underflow (late memory) */ +#define TX_LCOL 0x1000 /* Late collision */ +#define TX_LCAR 0x0400 /* Loss of Carrier */ +#define TX_RTRY 0x0200 /* Failed after 16 retransmissions */ +#define TX_TDR 0x003f /* Time-domain-reflectometer-value */ + + +/* + * Structures used for Communication with the LANCE + */ + +/* LANCE Initialize Block */ + +struct init_block +{ + unsigned short mode; /* Mode Register */ + unsigned char paddr[6]; /* Physical Address (MAC) */ + unsigned char laddr[8]; /* Logical Filter Address (not used) */ + unsigned int rdrp; /* Receive Descriptor Ring pointer */ + unsigned int tdrp; /* Transmit Descriptor Ring pointer */ +}; + + +/* Receive Message Descriptor Entry */ + +struct rmd +{ + union rmd_u + { + unsigned long buffer; /* Address of buffer */ + struct rmd_s + { + unsigned char unused[3]; + unsigned volatile char status; /* Status Bits */ + } s; + } u; + volatile short blen; /* Buffer Length (two's complement) */ + unsigned short mlen; /* Message Byte Count */ +}; + + +/* Transmit Message Descriptor Entry */ + +struct tmd +{ + union tmd_u + { + unsigned long buffer; /* Address of buffer */ + struct tmd_s + { + unsigned char unused[3]; + unsigned volatile char status; /* Status Bits */ + } s; + } u; + unsigned short blen; /* Buffer Length (two's complement) */ + unsigned volatile short status2; /* Error Status Bits */ +}; + +#endif /* End of SK_G16_H */ diff --git a/src/drivers/net/skel.c b/src/drivers/net/skel.c new file mode 100644 index 00000000..4b7f103a --- /dev/null +++ b/src/drivers/net/skel.c @@ -0,0 +1,200 @@ +/************************************************************************** +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 "pci.h" +/* to get the ISA support functions, if this is an ISA NIC */ +#include "isa.h" + +/* NIC specific static variables go here */ + +/************************************************************************** +POLL - Wait for a frame +***************************************************************************/ +static int skel_poll(struct nic *nic, int retrieve) +{ + /* Work out whether or not there's an ethernet packet ready to + * read. Return 0 if not. + */ + /* + if ( ! ) 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 = ; + memcpy ( nic->packet, , ); + return 1; + */ + + return 0; /* Remove this line once this method is implemented */ +} + +/************************************************************************** +TRANSMIT - Transmit a frame +***************************************************************************/ +static void skel_transmit( + struct nic *nic, + const char *dest, /* Destination */ + unsigned int type, /* Type */ + unsigned int size, /* size */ + const char *packet) /* Packet */ +{ + /* Transmit packet to dest MAC address. You will need to + * construct the link-layer header (dest MAC, source MAC, + * type). + */ + /* + unsigned int nstype = htons ( type ); + memcpy ( , dest, ETH_ALEN ); + memcpy ( + ETH_ALEN, nic->node_addr, ETH_ALEN ); + memcpy ( + 2 * ETH_ALEN, &nstype, 2 ); + memcpy ( + ETH_HLEN, data, size ); + ( , size + ETH_HLEN ); + */ +} + +/************************************************************************** +DISABLE - Turn off ethernet interface +***************************************************************************/ +static void skel_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. + */ +} + +/************************************************************************** +IRQ - handle interrupts +***************************************************************************/ +static void skel_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. + */ + + 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; + } +} + +/************************************************************************** +PROBE - Look for an adapter, this routine's visible to the outside +***************************************************************************/ + +#define board_found 1 +#define valid_link 0 +static int skel_probe(struct dev *dev, struct pci_device *pci) +{ + struct nic *nic = (struct nic *)dev; + + if (board_found && valid_link) + { + /* store NIC parameters */ + nic->ioaddr = pci->ioaddr & ~3; + nic->irqno = pci->irq; + /* point to NIC specific routines */ + dev->disable = skel_disable; + nic->poll = skel_poll; + nic->transmit = skel_transmit; + nic->irq = skel_irq; + return 1; + } + /* else */ + return 0; +} + +static struct pci_id skel_nics[] = { +PCI_ROM(0x0000, 0x0000, "skel-pci", "Skeleton PCI Adaptor"), +}; + +static struct pci_driver skel_driver __pci_driver = { + .type = NIC_DRIVER, + .name = "SKELETON/PCI", + .probe = skel_probe, + .ids = skel_nics, + .id_count = sizeof(skel_nics)/sizeof(skel_nics[0]), + .class = 0, +}; + +/************************************************************************** +PROBE - Look for an adapter, this routine's visible to the outside +***************************************************************************/ +static int skel_isa_probe(struct dev *dev, unsigned short *probe_addrs) +{ + struct nic *nic = (struct nic *)dev; + /* if probe_addrs is 0, then routine can use a hardwired default */ + if (board_found && valid_link) + { + /* point to NIC specific routines */ + dev->disable = skel_disable; + nic->poll = skel_poll; + nic->transmit = skel_transmit; + + /* Report the ISA pnp id of the board */ + dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR); + dev->devid.vendor_id = htons(0x1234); + return 1; + } + /* else */ + return 0; +} + +ISA_ROM("skel-isa", "Skeleton ISA driver") +static struct isa_driver skel_isa_driver __isa_driver = { + .type = NIC_DRIVER, + .name = "SKELETON/ISA", + .probe = skel_isa_probe, + .ioaddrs = 0, +}; + diff --git a/src/drivers/net/smc9000.c b/src/drivers/net/smc9000.c new file mode 100644 index 00000000..a05521e8 --- /dev/null +++ b/src/drivers/net/smc9000.c @@ -0,0 +1,544 @@ +#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 + * Based on the Linux SMC9000 driver, smc9194.c by Eric Stahlman + * 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. + * + * "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 + * Daniel Engström + * + * History + * 98-09-25 Daniel Engström Etherboot driver crated from Eric's + * Linux driver. + * + *---------------------------------------------------------------------------*/ +#define LINUX_OUT_MACROS 1 +#define SMC9000_VERBOSE 1 +#define SMC9000_DEBUG 0 + +#include "etherboot.h" +#include "nic.h" +#include "isa.h" +#include "smc9000.h" + +# define _outb outb +# define _outw outw + +static const char smc9000_version[] = "Version 0.99 98-09-30"; +static unsigned int smc9000_base=0; +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", + NULL, NULL, NULL, + NULL, NULL, NULL +}; +static const char smc91c96_id[] = "SMC91C96"; + +/* + * 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: smc_probe( int ioaddr ) + * + * Purpose: + * Tests to see if a given ioaddr points to an SMC9xxx chip. + * Returns a 0 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 smc_probe( int 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 -1; + } + /* 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 -1; + } + + /* 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)) { +#ifdef SMC9000_VERBOSE + printf("SMC9000: IOADDR %hX doesn't match configuration (%hX)." + "Probably not a SMC chip\n", + ioaddr, base_address_register >> 3 & 0x3E0); +#endif + /* well, the base address register didn't match. Must not have + * been a SMC chip after all. */ + return -1; + } + + + /* 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... */ +#ifdef SMC9000_VERBOSE + printf("SMC9000: IO %hX: Unrecognized revision register:" + " %hX, Contact author.\n", ioaddr, revision_register); +#endif + return -1; + } + + /* 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 0; +} + + +/************************************************************************** + * 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 ) { +#ifdef SMC9000_VERBOSE + printf("SMC9000: Far too big packet error. \n"); +#endif + return; + } + + /* dont try more than, say 30 times */ + for (i=0;i<30;i++) { + /* now, try to allocate the memory */ + SMC_SELECT_BANK(smc9000_base, 2); + _outw(MC_ALLOC | numPages, smc9000_base + MMU_CMD); + + status = 0; + /* wait for the memory allocation to finnish */ + for (time_out = currticks() + 5*TICKS_PER_SEC; currticks() < time_out; ) { + status = inb(smc9000_base + INTERRUPT); + if ( status & IM_ALLOC_INT ) { + /* acknowledge the interrupt */ + _outb(IM_ALLOC_INT, smc9000_base + 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, smc9000_base + MMU_CMD); + } + } + + /* If I get here, I _know_ there is a packet slot waiting for me */ + packet_no = inb(smc9000_base + 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, smc9000_base + PNR_ARR); + + /* point to the beginning of the packet */ + _outw(PTR_AUTOINC, smc9000_base + 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, smc9000_base + DATA_1 ); + + /* send the packet length ( +6 for status words, length, and ctl) */ + _outb((length+6) & 0xFF, smc9000_base + DATA_1); + _outb((length+6) >> 8 , smc9000_base + DATA_1); + + /* Write the contents of the packet */ + + /* The ethernet header first... */ + outsw(smc9000_base + DATA_1, d, ETH_ALEN >> 1); + outsw(smc9000_base + DATA_1, nic->node_addr, ETH_ALEN >> 1); + _outw(htons(t), smc9000_base + DATA_1); + + /* ... the data ... */ + outsw(smc9000_base + DATA_1 , p, s >> 1); + + /* ... and the last byte, if there is one. */ + if ((s & 1) == 0) { + _outw(0, smc9000_base + DATA_1); + } else { + _outb(p[s-1], smc9000_base + DATA_1); + _outb(0x20, smc9000_base + DATA_1); + } + + /* and let the chipset deal with it */ + _outw(MC_ENQUEUE , smc9000_base + MMU_CMD); + + status = 0; time_out = currticks() + 5*TICKS_PER_SEC; + do { + status = inb(smc9000_base + INTERRUPT); + + if ((status & IM_TX_INT ) != 0) { + word tx_status; + + /* ack interrupt */ + _outb(IM_TX_INT, smc9000_base + INTERRUPT); + + packet_no = inw(smc9000_base + FIFO_PORTS); + packet_no &= 0x7F; + + /* select this as the packet to read from */ + _outb( packet_no, smc9000_base + PNR_ARR ); + + /* read the first word from this packet */ + _outw( PTR_AUTOINC | PTR_READ, smc9000_base + POINTER ); + + tx_status = inw( smc9000_base + DATA_1 ); + + if (0 == (tx_status & TS_SUCCESS)) { +#ifdef SMC9000_VERBOSE + printf("SMC9000: TX FAIL STATUS: %hX \n", tx_status); +#endif + /* re-enable transmit */ + SMC_SELECT_BANK(smc9000_base, 0); + _outw(inw(smc9000_base + TCR ) | TCR_ENABLE, smc9000_base + TCR ); + } + + /* kill the packet */ + SMC_SELECT_BANK(smc9000_base, 2); + _outw(MC_FREEPKT, smc9000_base + MMU_CMD); + + return; + } + }while(currticks() < time_out); + + printf("SMC9000: Waring TX timed out, resetting board\n"); + smc_reset(smc9000_base); + return; +} + +/************************************************************************** + * ETH_POLL - Wait for a frame + ***************************************************************************/ +static int smc9000_poll(struct nic *nic, int retrieve) +{ + if(!smc9000_base) + return 0; + + SMC_SELECT_BANK(smc9000_base, 2); + if (inw(smc9000_base + 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, smc9000_base + POINTER); + + /* First read the status and check that we're ok */ + if (!(inw(smc9000_base + DATA_1) & RS_ERRORS)) { + /* Next: read the packet length and mask off the top bits */ + nic->packetlen = (inw(smc9000_base + 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(smc9000_base + 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, smc9000_base + MMU_CMD); + return 1; + } + + printf("SMC9000: RX error\n"); + /* error or good, tell the card to get rid of this packet */ + _outw(MC_RELEASE, smc9000_base + MMU_CMD); + return 0; +} + +static void smc9000_disable(struct dev *dev __unused) +{ + if(!smc9000_base) + return; + + smc_reset(smc9000_base); + + /* no more interrupts for me */ + SMC_SELECT_BANK(smc9000_base, 2); + _outb( 0, smc9000_base + INT_MASK); + + /* and tell the card to stay away from that nasty outside world */ + SMC_SELECT_BANK(smc9000_base, 0); + _outb( RCR_CLEAR, smc9000_base + RCR ); + _outb( TCR_CLEAR, smc9000_base + 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; + } +} + +/************************************************************************** + * ETH_PROBE - Look for an adapter + ***************************************************************************/ + +static int smc9000_probe(struct dev *dev, unsigned short *probe_addrs) +{ + struct nic *nic = (struct nic *)dev; + unsigned short revision; + int memory; + int media; + const char * version_string; + const char * if_string; + int i; + + /* + * 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. Keep in + * mind that the array must end in zero. + */ + static unsigned short portlist[] = { +#ifdef SMC9000_SCAN + SMC9000_SCAN, +#else + 0x200, 0x220, 0x240, 0x260, 0x280, 0x2A0, 0x2C0, 0x2E0, + 0x300, 0x320, 0x340, 0x360, 0x380, 0x3A0, 0x3C0, 0x3E0, +#endif + 0 }; + + /* if no addresses supplied, fall back on defaults */ + if (probe_addrs == 0 || probe_addrs[0] == 0) + probe_addrs = portlist; + + /* check every ethernet address */ + for (i = 0; probe_addrs[i]; i++) { + /* check this specific address */ + if (smc_probe(probe_addrs[i]) == 0) + smc9000_base = probe_addrs[i]; + } + + /* couldn't find anything */ + if(0 == smc9000_base) + goto out; + + nic->irqno = 0; + nic->ioaddr = smc9000_base; + + /* + * Get the MAC address ( bank 1, regs 4 - 9 ) + */ + SMC_SELECT_BANK(smc9000_base, 1); + for ( i = 0; i < 6; i += 2 ) { + word address; + + address = inw(smc9000_base + ADDR0 + i); + nic->node_addr[i+1] = address >> 8; + nic->node_addr[i] = address & 0xFF; + } + + + /* get the memory information */ + SMC_SELECT_BANK(smc9000_base, 0); + memory = ( inw(smc9000_base + MCR) >> 9 ) & 0x7; /* multiplier */ + memory *= 256 * (inw(smc9000_base + 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(smc9000_base, 3); + revision = inw(smc9000_base + 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.... */ + goto out; + } + + /* is it using AUI or 10BaseT ? */ + SMC_SELECT_BANK(smc9000_base, 1); + if (inw(smc9000_base + 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(smc9000_base); + + printf("SMC9000 %s\n", smc9000_version); +#ifdef SMC9000_VERBOSE + printf("Copyright (C) 1998 Daniel Engstr\x94m\n"); + printf("Copyright (C) 1996 Eric Stahlman\n"); +#endif + + printf("%s rev:%d I/O port:%hX Interface:%s RAM:%d bytes \n", + version_string, revision & 0xF, + smc9000_base, if_string, memory ); + /* + * Print the Ethernet address + */ + printf("Ethernet MAC address: %!\n", nic->node_addr); + + SMC_SELECT_BANK(smc9000_base, 0); + + /* see the header file for options in TCR/RCR NORMAL*/ + _outw(TCR_NORMAL, smc9000_base + TCR); + _outw(RCR_NORMAL, smc9000_base + RCR); + + /* Select which interface to use */ + SMC_SELECT_BANK(smc9000_base, 1); + if ( media == 1 ) { + _outw( inw( smc9000_base + CONFIG ) & ~CFG_AUI_SELECT, + smc9000_base + CONFIG ); + } + else if ( media == 2 ) { + _outw( inw( smc9000_base + CONFIG ) | CFG_AUI_SELECT, + smc9000_base + CONFIG ); + } + + dev->disable = smc9000_disable; + nic->poll = smc9000_poll; + nic->transmit = smc9000_transmit; + nic->irq = smc9000_irq; + + /* Based on PnP ISA map */ + dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR); + dev->devid.device_id = htons(0x8228); + + return 1; + +out: +#ifdef SMC9000_VERBOSE + /* printf("No SMC9000 adapters found\n"); */ +#endif + smc9000_base = 0; + + return (0); +} + +static struct isa_driver smc9000_driver __isa_driver = { + .type = NIC_DRIVER, + .name = "SMC9000", + .probe = smc9000_probe, + .ioaddrs = 0, +}; diff --git a/src/drivers/net/smc9000.h b/src/drivers/net/smc9000.h new file mode 100644 index 00000000..ac7f9163 --- /dev/null +++ b/src/drivers/net/smc9000.h @@ -0,0 +1,205 @@ +/*------------------------------------------------------------------------ + * 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 + * Erik Stahlman + * + * 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 */ + +/* 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 # ) */ + + +/* 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) + + +/*------------------------------------------------------------------------- + * 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/src/drivers/net/sundance.c b/src/drivers/net/sundance.c new file mode 100644 index 00000000..4912a121 --- /dev/null +++ b/src/drivers/net/sundance.c @@ -0,0 +1,891 @@ +/************************************************************************** +* +* sundance.c -- Etherboot device driver for the Sundance ST201 "Alta". +* Written 2002-2002 by Timothy Legge +* +* 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 "pci.h" +#include "timer.h" +#include "mii.h" + +#define drv_version "v1.12" +#define drv_date "2004-03-21" + +typedef unsigned char u8; +typedef signed char s8; +typedef unsigned short u16; +typedef signed short s16; +typedef unsigned int u32; +typedef signed int s32; + +#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? */ +}; + +//#define EDEBUG +#ifdef EDEBUG +#define dprintf(x) printf x +#else +#define dprintf(x) +#endif + + +/* 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]; + +/* Create a static buffer of size PKT_BUF_SZ for each TX Descriptor. + All descriptors point to a part of this buffer */ +static unsigned char txb[PKT_BUF_SZ * 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 Descriptor. + All descriptors point to a part of this buffer */ +static unsigned char rxb[RX_RING_SIZE * PKT_BUF_SZ]; + +/* 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 +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; + dprintf(("%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]; + + dprintf(("%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 +***************************************************************************/ +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) { + dprintf(("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 dev *dev __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); +} + + + +/************************************************************************** +PROBE - Look for an adapter, this routine's visible to the outside +***************************************************************************/ +static int sundance_probe(struct dev *dev, struct pci_device *pci) +{ + struct nic *nic = (struct nic *) dev; + 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->name, pci->vendor, pci->dev_id); + + /* 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->name; + sdc->mtu = mtu; + + pci_read_config_byte(pci, PCI_REVISION_ID, &sdc->pci_rev_id); + dprintf(("Device revision id: %hx\n", sdc->pci_rev_id)); + /* Print out some hardware info */ + printf("%s: %! at ioaddr %hX, ", pci->name, nic->node_addr, 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++; + dprintf + (("%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 */ + dprintf(("ASIC Control is %x.\n", inl(BASE + ASICCtrl))); + outw(0x007f, BASE + ASICCtrl + 2); + dprintf(("ASIC Control is now %x.\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 */ + dev->disable = sundance_disable; + nic->poll = sundance_poll; + nic->transmit = sundance_transmit; + nic->irqno = pci->irq; + nic->irq = sundance_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_id sundance_nics[] = { + PCI_ROM(0x13f0, 0x0201, "sundance", "ST201 Sundance 'Alta' based Adaptor"), + PCI_ROM(0x1186, 0x1002, "dfe530txs", "D-Link DFE530TXS (Sundance ST201 Alta)"), +}; + +static struct pci_driver sundance_driver __pci_driver = { + .type = NIC_DRIVER, + .name = "SUNDANCE/PCI", + .probe = sundance_probe, + .ids = sundance_nics, + .id_count = sizeof(sundance_nics) / sizeof(sundance_nics[0]), + .class = 0, +}; diff --git a/src/drivers/net/tg3.c b/src/drivers/net/tg3.c new file mode 100644 index 00000000..e1db54e6 --- /dev/null +++ b/src/drivers/net/tg3.c @@ -0,0 +1,3394 @@ +/* $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 + */ + +#include "etherboot.h" +#include "nic.h" +#include "pci.h" +#include "timer.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 + +struct tg3 tg3; + +/* Dummy defines for error handling */ +#define EBUSY 1 +#define ENODEV 2 +#define EINVAL 3 +#define ENOMEM 4 + + +/* 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) + + +static 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]; +} tg3_bss; + +/** + * 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, ®32); + 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, ®32); + 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=%x\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", + 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=%x\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 */ + + /* 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->dev_id == PCI_DEVICE_ID_TIGON3_5901) || + (tp->pdev->dev_id == 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 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; + + /* 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 */ + poll_interruptions(); + 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 */ + 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 */ + txd = &tp->tx_ring[entry]; + txd->addr_hi = 0; /* Etherboot runs under 4GB */ + txd->addr_lo = virt_to_bus(&frame[frame_idx]); + 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 dev *dev __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; + } +} + +/************************************************************************** +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 dev *dev, struct pci_device *pdev) +{ + struct nic *nic = (struct nic *)dev; + struct tg3 *tp = &tg3; + unsigned long tg3reg_base, tg3reg_len; + int i, err, pm_cap; + + if (pdev == 0) + return 0; + + memset(tp, 0, sizeof(*tp)); + + adjust_pci_device(pdev); + + nic->irqno = 0; + nic->ioaddr = pdev->ioaddr & ~3; + + /* 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; + } + printf("Ethernet addr: %!\n", 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; + } + + dev->disable = tg3_disable; + nic->poll = tg3_poll; + nic->transmit = tg3_transmit; + nic->irq = tg3_irq; + + return 1; + + err_out_iounmap: + iounmap((void *)tp->regs); + return 0; + err_out_disable: + tg3_disable(dev); + return 0; +} + +static struct pci_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"), +}; + +static struct pci_driver tg3_driver __pci_driver = { + .type = NIC_DRIVER, + .name = "TG3", + .probe = tg3_probe, + .ids = tg3_nics, + .id_count = sizeof(tg3_nics)/sizeof(tg3_nics[0]), + .class = 0, +}; diff --git a/src/drivers/net/tg3.h b/src/drivers/net/tg3.h new file mode 100644 index 00000000..6e05b9cc --- /dev/null +++ b/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_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/src/drivers/net/tlan.c b/src/drivers/net/tlan.c new file mode 100644 index 00000000..0f216ed4 --- /dev/null +++ b/src/drivers/net/tlan.c @@ -0,0 +1,1722 @@ +/************************************************************************** +* +* tlan.c -- Etherboot device driver for the Texas Instruments ThunderLAN +* Written 2003-2003 by Timothy Legge +* +* 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 +***************************************************************************/ + +/* 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 "pci.h" +#include "timer.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)) + +//#define EDEBUG +#ifdef EDEBUG +#define dprintf(x) printf x +#else +#define dprintf(x) +#endif + +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, 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); + + +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 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 TLanList tx_ring[TLAN_NUM_TX_LISTS]; +static unsigned char txb[TLAN_MAX_FRAME_SIZE * TLAN_NUM_TX_LISTS]; + +struct TLanList rx_ring[TLAN_NUM_RX_LISTS]; +static unsigned char rxb[TLAN_MAX_FRAME_SIZE * TLAN_NUM_RX_LISTS]; + +typedef u8 TLanBuffer[TLAN_MAX_FRAME_SIZE]; + +int chip_idx; + +/***************************************************************** +* TLAN Private Information Structure +* +****************************************************************/ +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; + +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. +* +**************************************************************/ + +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; + dprintf(("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); + + dprintf(("TLAN: %s: Link active with ", + priv->nic_name)); + if (!(tlphy_par & TLAN_PHY_AN_EN_STAT)) { + dprintf(("forced 10%sMbps %s-Duplex\n", + tlphy_par & TLAN_PHY_SPEED_100 ? "" + : "0", + tlphy_par & TLAN_PHY_DUPLEX_FULL ? + "Full" : "Half")); + } else { + dprintf + (("AutoNegotiation enabled, at 10%sMbps %s-Duplex\n", + tlphy_par & TLAN_PHY_SPEED_100 ? "" : + "0", + tlphy_par & TLAN_PHY_DUPLEX_FULL ? + "Full" : "Half")); + dprintf(("TLAN: Partner capability: ")); + for (i = 5; i <= 10; i++) + if (partner & (1 << i)) + dprintf(("%s", media[i - 5])); + dprintf(("\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) { + dprintf(("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 { + dprintf + (("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; + + dprintf((".%d.", framesize)); + + memcpy(nic->packet, rxb + + (priv->cur_rx * TLAN_MAX_FRAME_SIZE), nic->packetlen); + + rx_ring[entry].cStat = 0; + + dprintf(("%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); + + dprintf(("AC: 0x%hX\n", inw(BASE + TLAN_CH_PARM))); + dprintf(("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; +#ifdef EBDEBUG + u16 host_int = inw(BASE + TLAN_HOST_INT); +#endif + int entry = 0; + + dprintf(("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=%d Tail=%d)\n", + priv->nic_name, priv->txList, 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; + + dprintf(("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) { + dprintf(("Out buffer\n")); + (priv->txList + (TLAN_NUM_TX_LISTS - 1))->forward = + virt_to_le32desc(tail_list); + } else { + dprintf(("Fix this \n")); + (priv->txList + (priv->txTail - 1))->forward = + virt_to_le32desc(tail_list); + } + } + + CIRC_INC(priv->txTail, TLAN_NUM_TX_LISTS); + + dprintf(("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 dev *dev __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 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 dev *dev, struct pci_device *pci) +{ + struct nic *nic = (struct nic *) dev; + u16 data = 0; + int err; + int i; + + if (pci->ioaddr == 0) + return 0; + + nic->irqno = 0; + nic->ioaddr = pci->ioaddr & ~3; + + BASE = pci->ioaddr; + + printf("tlan.c: Found %s, Vendor 0x%hX, Device 0x%hX\n", + pci->name, pci->vendor, pci->dev_id); + + /* 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->dev_id << 16) | pci->vendor) == + (tlan_pci_tbl[i].id.pci & 0xffffffff)) { + chip_idx = i; + break; + } + i++; + } + + priv->vendor_id = pci->vendor; + priv->dev_id = pci->dev_id; + priv->nic_name = pci->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->name, err); + } else + /* Print out some hardware info */ + printf("%s: %! at ioaddr %hX, ", + pci->name, 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 */ + + dev->disable = tlan_disable; + nic->poll = tlan_poll; + nic->transmit = tlan_transmit; + nic->irq = tlan_irq; + 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, 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", 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; + dprintf(("%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; + + dprintf(("%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]; + + dprintf(("%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]; + dprintf(("%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. + */ + dprintf(("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; + } + + dprintf(("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); + dprintf + (("TLAN: Starting internal PHY with FULL-DUPLEX\n")); + } else { + TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, + MII_GC_AUTOENB); + dprintf + (("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_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"), +}; + +static struct pci_driver tlan_driver __pci_driver = { + .type = NIC_DRIVER, + .name = "TLAN/PCI", + .probe = tlan_probe, + .ids = tlan_nics, + .id_count = sizeof(tlan_nics) / sizeof(tlan_nics[0]), + .class = 0, +}; diff --git a/src/drivers/net/tlan.h b/src/drivers/net/tlan.h new file mode 100644 index 00000000..35753e6a --- /dev/null +++ b/src/drivers/net/tlan.h @@ -0,0 +1,536 @@ +/************************************************************************** +* +* tlan.c -- Etherboot device driver for the Texas Instruments ThunderLAN +* Written 2003-2003 by Timothy Legge +* +* 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 +***************************************************************************/ + +/* +#include +#include +#include +*/ + +typedef unsigned char u8; +typedef signed char s8; +typedef unsigned short u16; +typedef signed short s16; +typedef unsigned int u32; +typedef signed int s32; + /***************************************************************** + * 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. */ + +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 */ + + + + +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 */ + + + + +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 */ + + + + +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)); + +} + + + + +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)); + +} + + + + +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 +inline void TLan_ClearBit(u8 bit, u16 port) +{ + outb_p(inb_p(port) & ~bit, port); +} + + + + +inline int TLan_GetBit(u8 bit, u16 port) +{ + return ((int) (inb_p(port) & bit)); +} + + + + +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 */ +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 */ + +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 ) ) ) + +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/src/drivers/net/tulip.c b/src/drivers/net/tulip.c new file mode 100644 index 00000000..23fca665 --- /dev/null +++ b/src/drivers/net/tulip.c @@ -0,0 +1,2082 @@ +/* -*- Mode:C; c-basic-offset:4; -*- */ + +/* + Tulip and clone Etherboot Driver + + By Marty Connor (mdc@thinguin.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 */ +/*********************************************************************/ + +/* + 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 "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 + +typedef uint8_t u8; +typedef int8_t s8; +typedef uint16_t u16; +typedef int16_t s16; +typedef uint32_t u32; +typedef int32_t s32; + +/* 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 +}; + +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 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 }, + { 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 }, + { 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; + +/* Note: transmit and receive buffers must be longword aligned and + longword divisable */ + +#define TX_RING_SIZE 2 +static struct tulip_tx_desc tx_ring[TX_RING_SIZE] __attribute__ ((aligned(4))); +static unsigned char txb[BUFLEN] __attribute__ ((aligned(4))); + +#define RX_RING_SIZE 4 +static struct tulip_rx_desc rx_ring[RX_RING_SIZE] __attribute__ ((aligned(4))); +static unsigned char rxb[RX_RING_SIZE * BUFLEN] __attribute__ ((aligned(4))); + +static 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; +} tpx; + +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 dev *dev, 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 dev *dev); +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) +{ + 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 dev *dev) +{ + struct nic *nic = (struct nic *)dev; +#ifdef TULIP_DEBUG_WHERE + whereami("tulip_disable\n"); +#endif + + /* merge reset and disable */ + 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; + } +} + +/*********************************************************************/ +/* eth_probe - Look for an adapter */ +/*********************************************************************/ +static int tulip_probe(struct dev *dev, struct pci_device *pci) +{ + struct nic *nic = (struct nic *)dev; + 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 = &tpx; + + tp->vendor_id = pci->vendor; + tp->dev_id = pci->dev_id; + tp->nic_name = pci->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_id, 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) + pcibios_write_config_dword(pci->bus, pci->devfn, 0x40, 0x00000000); + + if (inl(ioaddr + CSR5) == 0xFFFFFFFF) { + printf("%s: The Tulip chip at %X is not functioning.\n", + tp->nic_name, ioaddr); + return 0; + } + + pcibios_read_config_byte(pci->bus, pci->devfn, PCI_REVISION, &chip_rev); + + printf("%s: [chip: %s] rev %d at %hX\n", tp->nic_name, + tulip_tbl[chip_idx].chip_name, chip_rev, 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]; + + printf("%s: %! at ioaddr %hX\n", tp->nic_name, nic->node_addr, 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) { + 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); + + dev->disable = tulip_disable; + nic->poll = tulip_poll; + nic->transmit = tulip_transmit; + nic->irq = tulip_irq; + + /* 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: + 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_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, "tulip-2774", "Tulip 0x104a 0x2774"), +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"), +}; + +static struct pci_driver tulip_driver __pci_driver = { + .type = NIC_DRIVER, + .name = "Tulip", + .probe = tulip_probe, + .ids = tulip_nics, + .id_count = sizeof(tulip_nics)/sizeof(tulip_nics[0]), + .class = 0, +}; diff --git a/src/drivers/net/tulip.txt b/src/drivers/net/tulip.txt new file mode 100644 index 00000000..68b7b3b7 --- /dev/null +++ b/src/drivers/net/tulip.txt @@ -0,0 +1,53 @@ +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@thinguin.org) and the netboot list (netboot@baghira.han.de) +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@thinguin.org +http://www.thinguin.org/ diff --git a/src/drivers/net/via-rhine.c b/src/drivers/net/via-rhine.c new file mode 100644 index 00000000..d5e4812e --- /dev/null +++ b/src/drivers/net/via-rhine.c @@ -0,0 +1,1426 @@ +/* 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 "pci.h" +#include "timer.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; + +}; + + +/* 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 dev *dev); +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; + + 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; + + load_timer2(2*TICKS_PER_MS); + while (byMIItemp != 0 && timer2_running()) + { + 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; + + + 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; + + load_timer2(2*TICKS_PER_MS); + while (byMIItemp != 0 && timer2_running()) + { + 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; + + load_timer2(2*TICKS_PER_MS); + while (byMIItemp != 0 && timer2_running()) + { + 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++) + { + inb (0x61); + inb (0x61); + inb (0x61); + 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 int +rhine_probe (struct dev *dev, struct pci_device *pci) +{ + struct nic *nic = (struct nic *)dev; + struct rhine_private *tp = (struct rhine_private *) nic->priv_data; + if (!pci->ioaddr) + return 0; + rhine_probe1 (nic, pci, pci->ioaddr, pci->dev_id, -1); + + adjust_pci_device(pci); + rhine_reset (nic); + + dev->disable = rhine_disable; + nic->poll = rhine_poll; + nic->transmit = rhine_transmit; + nic->irqno = pci->irq; + nic->irq = rhine_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. */ + 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); + printf ("IO address %hX Ethernet Address: %!\n", ioaddr, nic->node_addr); + + /* restart MII auto-negotiation */ + WriteMII (0, 9, 1, ioaddr); + printf ("Analyzing Media type,this will 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 dev *dev) +{ + struct nic *nic = (struct nic *)dev; + struct rhine_private *tp = (struct rhine_private *) nic->priv_data; + int ioaddr = tp->ioaddr; + + /* merge reset and disable */ + 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; + + static char buf1[TX_RING_SIZE * PKT_BUF_SZ + 32]; + static char buf2[RX_RING_SIZE * PKT_BUF_SZ + 32]; + static char desc1[TX_RING_SIZE * sizeof (struct rhine_tx_desc) + 32]; + static char desc2[RX_RING_SIZE * sizeof (struct rhine_rx_desc) + 32]; + + /* printf ("rhine_reset\n"); */ + /* Soft reset the chip. */ + /*outb(CmdReset, ioaddr + ChipCmd); */ + + tx_bufs_tmp = (int) buf1; + tx_ring_tmp = (int) desc1; + rx_bufs_tmp = (int) buf2; + rx_ring_tmp = (int) desc2; + + /* 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; + + + /*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 + { + load_timer2(10*TICKS_PER_MS); + /* Wait until transmit is finished or timeout*/ + while((tp->tx_ring[entry].tx_status.bits.own_bit !=0) && timer2_running()) + ; + + 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 pci_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 */ +}; + +static struct pci_driver rhine_driver __pci_driver = { + .type = NIC_DRIVER, + .name = "VIA 86C100", + .probe = rhine_probe, + .ids = rhine_nics, + .id_count = sizeof(rhine_nics)/sizeof(rhine_nics[0]), + .class = 0, +}; + +/* EOF via-rhine.c */ diff --git a/src/drivers/net/w89c840.c b/src/drivers/net/w89c840.c new file mode 100644 index 00000000..27a81eda --- /dev/null +++ b/src/drivers/net/w89c840.c @@ -0,0 +1,958 @@ +/* + * Etherboot - BOOTP/TFTP Bootstrap Program + * + * w89c840.c -- This file implements the winbond-840 driver for etherboot. + * + */ + +/* + * Adapted by Igor V. Kovalenko + * -- + * OR + * -- + * 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 "pci.h" +#include "timer.h" + +static const char *w89c840_version = "driver Version 0.94 - December 12, 2003"; + +typedef unsigned char u8; +typedef signed char s8; +typedef unsigned short u16; +typedef signed short s16; +typedef unsigned int u32; +typedef signed int s32; + +/* 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 '%'<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*TICKS_PER_MS) + +#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]; +static char rx_packet[PKT_BUF_SZ * RX_RING_SIZE]; +static char tx_packet[PKT_BUF_SZ * TX_RING_SIZE]; + +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 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, 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; + + /* 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 (tx_packet, d, ETH_ALEN); /* dst */ + memcpy (tx_packet + ETH_ALEN, nic->node_addr, ETH_ALEN);/* src */ + + *((char *) tx_packet + 12) = t >> 8; /* type */ + *((char *) tx_packet + 13) = t; + + memcpy (tx_packet + ETH_HLEN, p, s); + s += ETH_HLEN; + + while (s < ETH_ZLEN) + *((char *) tx_packet + ETH_HLEN + (s++)) = 0; + + w840private.tx_ring[entry].buffer1 = virt_to_le32desc(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; + + load_timer2(TX_TIMEOUT); + + { +#if defined W89C840_DEBUG + u32 intr_stat = 0; +#endif + while (1) { + +#if defined(W89C840_DEBUG) + decode_interrupt(intr_stat); +#endif + + while ( (transmit_status & DescOwn) && timer2_running()) { + + 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", w840private.tx_ring[entry].status); + + return; +} + +/************************************************************************** +w89c840_disable - Turn off ethernet interface +***************************************************************************/ +static void w89c840_disable(struct dev *dev) +{ + struct nic *nic = (struct nic *)dev; + /* merge reset and disable */ + 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; + } +} + +/************************************************************************** +w89c840_probe - Look for an adapter, this routine's visible to the outside +***************************************************************************/ +static int w89c840_probe(struct dev *dev, struct pci_device *p) +{ + struct nic *nic = (struct nic *)dev; + u16 sum = 0; + int i, j; + unsigned short value; + + if (p->ioaddr == 0) + return 0; + + ioaddr = p->ioaddr; + nic->ioaddr = p->ioaddr & ~3; + 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 */ + if (p->vendor == PCI_VENDOR_ID_WINBOND2 + && p->dev_id == PCI_DEVICE_ID_WINBOND2_89C840) { + + /* detected "Winbond W89c840 Fast Ethernet PCI NIC" */ + + } else if ( p->vendor == PCI_VENDOR_ID_COMPEX + && p->dev_id == 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->dev_id); + 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;inode_addr[i] = (eeprom[i/2] >> (8*(i&1))) & 0xff; + } + printf ("Ethernet addr: %!\n", 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 */ + dev->disable = w89c840_disable; + nic->poll = w89c840_poll; + nic->transmit = w89c840_transmit; + nic->irq = w89c840_irq; + + 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 = &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; +} + + +static struct pci_id w89c840_nics[] = { +PCI_ROM(0x1050, 0x0840, "winbond840", "Winbond W89C840F"), +PCI_ROM(0x11f6, 0x2011, "compexrl100atx", "Compex RL100ATX"), +}; + +static struct pci_driver w89c840_driver __pci_driver = { + .type = NIC_DRIVER, + .name = "W89C840F", + .probe = w89c840_probe, + .ids = w89c840_nics, + .id_count = sizeof(w89c840_nics)/sizeof(w89c840_nics[0]), + .class = 0, +}; diff --git a/src/drivers/net/wlan_compat.h b/src/drivers/net/wlan_compat.h new file mode 100644 index 00000000..a4f75e3a --- /dev/null +++ b/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 +#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/src/filo/Config b/src/filo/Config new file mode 100644 index 00000000..9f39b3d9 --- /dev/null +++ b/src/filo/Config @@ -0,0 +1,68 @@ +# !!! NOTE !!! +# Do NOT add spaces or comments at the end of option lines. +# It confuses some versions of make. + +# Image filename for automatic boot and optional command line parameter +#AUTOBOOT_FILE = "hda3:/boot/vmlinuz root=/dev/hda3 console=tty0 console=ttyS0,115200" +AUTOBOOT_FILE = "hda2:/boot/vmlinuz initrd=/boot/initrd pci=noacpi ro root=/dev/hda2 console=tty0 console=ttyS0,115200" +#AUTOBOOT_FILE = "mem@0xfff80000" +#AUTOBOOT_FILE = "hde1@0" +#AUTOBOOT_FILE = "uda1:/ram0_2.5_2.6.5_k8.2_mydisk7.elf" +#AUTOBOOT_FILE = "hda5:/boot/vmlinuz initrd=/boot/initrd ro root=/dev/hda7 console=tty0 console=ttyS0,115200" + +# Time in second before booting AUTOBOOT_FILE +AUTOBOOT_DELAY = 2 + +# Driver for hard disk, CompactFlash, and CD-ROM on IDE bus +IDE_DISK = 1 + +# Driver for USB disk +USB_DISK = 1 + +# Filesystems +# To make filo.zelf < 32 k, You may not enable JFS, MINIX, XFS +# Is anyone still using these file system? BY LYH +FSYS_EXT2FS = 1 +FSYS_FAT = 1 +#FSYS_JFS = 1 +#FSYS_MINIX = 1 +FSYS_REISERFS = 1 +#FSYS_XFS = 1 +FSYS_ISO9660 = 1 + +# Support for boot disk image in bootable CD-ROM (El Torito) +ELTORITO = 1 + +# PCI support +SUPPORT_PCI = 1 + + +# Debugging +#DEBUG_ALL = 1 +#DEBUG_ELFBOOT = 1 +#DEBUG_ELFNOTE = 1 +#DEBUG_LINUXBIOS = 1 +#DEBUG_MALLOC = 1 +#DEBUG_MULTIBOOT = 1 +#DEBUG_SEGMENT = 1 +#DEBUG_SYS_INFO = 1 +#DEBUG_TIMER = 1 +#DEBUG_BLOCKDEV = 1 +#DEBUG_PCI = 1 +#DEBUG_LINUXLOAD = 1 +#DEBUG_IDE = 1 +#DEBUG_USB = 1 +#DEBUG_ELTORITO = 1 + +# i386 options + +# Loader for standard Linux kernel image, a.k.a. /vmlinuz +LINUX_LOADER = 1 + +# Boot FILO from Multiboot loader (eg. GRUB) +# You need to modify i386/multiboot.c to use it. change mmrange to e820entries. +# By LYH +#MULTIBOOT_IMAGE = 1 + +# Use PCI Configuration Mechanism #1 (most boards) +PCI_CONFIG_1 = 1 diff --git a/src/filo/README.filo b/src/filo/README.filo new file mode 100644 index 00000000..1e801d31 --- /dev/null +++ b/src/filo/README.filo @@ -0,0 +1,125 @@ +This is FILO, a bootloader which loads boot images from local filesystem, +without help from legacy BIOS services. + +Expected usage is to flash it into the BIOS ROM together with LinuxBIOS. + +FEATURES + + - Supported boot devices: IDE hard disk and CD-ROM, and system memory (ROM) + - Supported filesystems: ext2, fat, jfs, minix, reiserfs, xfs, and iso9660 + - Supported image formats: ELF and [b]zImage (a.k.a. /vmlinuz) + - Supports boot disk image of El Torito bootable CD-ROM + - Supports loading image from raw device with user-specified offset + - Console on VGA + keyboard, serial port, or both + - Line editing with ^H, ^W and ^U keys to type arbitrary filename to boot + - Full support for the ELF Boot Proposal (where is it btw, Eric?) + - Auxiliary tool to compute checksum of ELF boot images + - Full 32-bit code, no BIOS calls + +REQUIREMENT + + Only i386 PC architecture is currently supported. + + x86-64 (AMD 64) machines in 32-bit mode should also work. + (It looks like LinuxBIOS uses 32-bit mode and Linux kernel does + the transition to 64-bit mode) + + I'm using a VIA EPIA 5000 mini-ITX board, with a 2.5" IDE hard disk + and a 32x CD-RW, for testing, and Bochs and VMware for development. + + Recent version of GNU toolchain is required to build. + I have tested with Debian/woody (gcc 2.95.4, binutils 2.12.90.0.1, + make 3.79.1) and Debian/sid (gcc 3.3.2, binutils 2.14.90.0.6, + make 3.80). + +INSTALL + + First invocation of make creates the default Config file. + $ make + Edit this file as you like. It's fairly straightforward (I hope). + $ vi Config + Then running make again will build filo.elf, the ELF boot image of FILO. + $ make + + Use filo.elf as your payload of LinuxBIOS, or a boot image for + Etherboot. + + If you enable MULTIBOOT_IMAGE option in Config, you can + also boot filo.elf from GNU GRUB or other Multiboot bootloader. + This feature is intended for testing or development purpose. + +USING + + When FILO starts, it displays "boot:" prompt. + At "boot:" prompt, type the name of your boot image, and optionally + the kernel parameter, in the form: + DEVICE:FILENAME[ PARAM] + for example: + boot: hda1:/vmlinuz root=/dev/hda1 + + Notation of DEVICE for IDE disk and CD-ROM is same as in Linux + (eg. hda1 means the first partition of master device on primary + IDE channel). + + FILENAME can be standard bzImage/zImage (vmlinuz) Linux kernels, + Linux-compatible images such as memtest.bin of Memtest86, + and any bootable ELF images, which include Linux kernel converted + by mkelfImage, Etherboot .elf and .zelf, Memtest86, FILO itself, etc. + + If AUTOBOOT_FILE is set in Config, FILO tries to boot this file + first, and falls back to boot: prompt if it fails. + + If AUTOBOOT_DELAY is also set, FILO waits for specified time in + seconds before booting AUTOBOOT_FILE. If key is pressed + during this time period, automatic boot is canceled. + Pressing key also cancels the delay, but in this case + AUTOBOOT_FILE is booted immediately. + + Even if AUTOBOOT_DELAY is not set, automatic boot can be disabled + by pressing key beforehand. + + FILO can also load separate initrd images along with vmlinuz + kernels. (For ELF kernel, initrd images are embedded into the + ELF file and cannot be altered). + To do so, add "initrd=NAME" parameter to the kernel command line. + NAME uses the same notation as kernel image name. + (eg. boot: hda1:/vmlinuz initrd=hda1:/root.gz root=/dev/ram) + + To boot an image in the BIOS flash (or whatever is mapped in the system + memory space), use the notation "mem@OFFSET[,LENGTH]", like: + boot: mem@0xfffe0000 + In this example, it loads the boot image from the last 128KB of BIOS + flash. + + The same notation can be used with IDE devices, eg: + boot: hda@512,697344 initrd=hda@1M,4M + In this case the 697344 bytes starting from second sector of IDE drive + is loaded as kernel, and 4M bytes of offset 1M bytes of the same disk + is loaded as initrd. + Note that when you load vmlinuz kernel or initrd this way, + you must specify the LENGTH parameter. You can omit it for ELF + images since they have segment length internally. + OFFSET and LENGTH parameters must be multiple of 512. + +BUG REPORTING + + If you have problem with FILO, set DEBUG_ALL in Config and send its + console output to me at . + +ACKNOWLEDGEMENTS + + Filesystem code is taken from GNU GRUB and patches for it. + IDE driver is originally taken from Etherboot. + Steve Gehlbach wrote the original bzImage loader for FILO. + + Besides, I have taken pieces of code and/or learned concepts + from various standalone programs, including GNU GRUB, Etherboot, + polled IDE patch by Adam Agnew, Memtest86, LinuxBIOS, and Linux. + I must say thanks to all the developers of these wonderful software, + especially to Eric Biederman for his great development work in this area. + +LICENSE + + Copyright (C) 2003 by SONE Takeshi and others. + This program is licensed under the terms of GNU General Public License. + See the COPYING file for details. diff --git a/src/filo/README.filo_in_etherboot b/src/filo/README.filo_in_etherboot new file mode 100644 index 00000000..dc203f63 --- /dev/null +++ b/src/filo/README.filo_in_etherboot @@ -0,0 +1,48 @@ +Moved from FILO into Etherboot, yhlu add boot from SATA disk and move usb boot framework +from Steven James baremetal in LinuxBIOS, also add the OHCI support. + + +1. refer to README.filo + but don't need to use make config. +2. CFLAG added + CONSOLE_BTEXT --- for btext console support + CONSOLE_PC_KBD --- for direct pc keyboard support + CONFIG_FILO --- It will make main call pci_init +3. to make: + make bin/filo.zelf + or + make bin/tg3--filo.zelf + + You can not use filo and ide_disk at the same time. + +Some input for boot: + +boot from BIOS ROM area +4G-128K +mem@0xfffe0000 +4G-512K +mem@0xfff80000 + +boot from suse +hda2:/boot/vmlinuz initrd=/boot/initrd ro root=/dev/hda2 console=tty0 console=ttyS0,115200 +for suse install from CD +hdc:/boot/loader/linux initrd=/boot/loader/initrd ramdisk_size=65536 splash=silent showopts console=tty0 console=ttyS0,115200 + +boot from RH +for RH install from CD +hdc:/isolinux/vmlinuz initrd=/isolinux/initrd.img expert nofb acpi=off devfs=nomount ramdisk_size=65536 console=ttyS0,115200 + +for serial ATA support (using port1 and port2 only) +1) if your kernel think SATA as SCSI +hde2:/boot/vmlinuz initrd=/boot/initrd ro root=/dev/sda2 console=tty0 console=ttyS0,115200 +2) if your kernel think SATA as normal IDE +hde2:/boot/vmlinuz initrd=/boot/initrd ro root=/dev/hde2 console=tty0 console=ttyS0,115200 + +for usb support +uda1:/ram0_2.5_2.6.5_k8.2_mydisk7.elf + + +Yinghai Lu yhlu@tyan.com + +to do: + add menu to filo boot diff --git a/src/filo/README.usb b/src/filo/README.usb new file mode 100644 index 00000000..1724e7bb --- /dev/null +++ b/src/filo/README.usb @@ -0,0 +1,15 @@ +It is from steven james's baremetal in linuxbios util. +yhlu seperate common functions from uhci.c to usb.c and create ohci.c to support ohci. +ohci.c mainly cames from kernel 2.4.22 dirvers/usb/host/usb-ohci.c. +it includes several parts +1. UHCI+OHCI--->USB: privide usb init and usb_control_msg and usb_bulk_msg interface +2. USB_SCSI: bulk only device +3. USB_X interface to FILO + +other changes in Etherboot +1. Add allot2 and forget2, it will produce the required aligned memory. + +todo: +1. EHCI support + +yhlu 6/2/2004 diff --git a/src/filo/drivers/ide_x.c b/src/filo/drivers/ide_x.c new file mode 100644 index 00000000..09c634e2 --- /dev/null +++ b/src/filo/drivers/ide_x.c @@ -0,0 +1,1129 @@ +/* Derived from Etherboot 5.1 */ + +#include +#include +#include +#include + +#define DEBUG_THIS DEBUG_IDE +#include + +#define IDE_MAX_CONTROLLERS 4 +#define IDE_MAX_DRIVES (IDE_MAX_CONTROLLERS * 2) + +#define BSY_SET_DURING_SPINUP 1 +/* + * UBL, The Universal Talkware Boot Loader + * Copyright (C) 2000 Universal Talkware Inc. + * Copyright (C) 2002 Eric Biederman + * + * 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 + * + * + */ +struct controller { + uint16_t cmd_base; + uint16_t ctrl_base; +}; + +struct harddisk_info { + struct controller *ctrl; + uint16_t heads; + uint16_t cylinders; + uint16_t sectors_per_track; + uint8_t model_number[41]; + uint8_t slave; + sector_t sectors; + int address_mode; +#define ADDRESS_MODE_CHS 0 +#define ADDRESS_MODE_LBA 1 +#define ADDRESS_MODE_LBA48 2 +#define ADDRESS_MODE_PACKET 3 + uint32_t hw_sector_size; + unsigned drive_exists : 1; + unsigned slave_absent : 1; + unsigned removable : 1; +}; + +#define IDE_SECTOR_SIZE 0x200 +#define CDROM_SECTOR_SIZE 0x800 + +#define IDE_BASE0 (0x1F0u) /* primary controller */ +#define IDE_BASE1 (0x170u) /* secondary */ +#define IDE_BASE2 (0x1E8u) /* third */ +#define IDE_BASE3 (0x168u) /* fourth */ +#define IDE_BASE4 (0x1E0u) /* fifth */ +#define IDE_BASE5 (0x160u) /* sixth */ + +#define IDE_REG_EXTENDED_OFFSET (0x204u) + +#define IDE_REG_DATA(ctrl) ((ctrl)->cmd_base + 0u) /* word register */ +#define IDE_REG_ERROR(ctrl) ((ctrl)->cmd_base + 1u) +#define IDE_REG_PRECOMP(ctrl) ((ctrl)->cmd_base + 1u) +#define IDE_REG_FEATURE(ctrl) ((ctrl)->cmd_base + 1u) +#define IDE_REG_SECTOR_COUNT(ctrl) ((ctrl)->cmd_base + 2u) +#define IDE_REG_SECTOR_NUMBER(ctrl) ((ctrl)->cmd_base + 3u) +#define IDE_REG_LBA_LOW(ctrl) ((ctrl)->cmd_base + 3u) +#define IDE_REG_CYLINDER_LSB(ctrl) ((ctrl)->cmd_base + 4u) +#define IDE_REG_LBA_MID(ctrl) ((ctrl)->cmd_base + 4u) +#define IDE_REG_CYLINDER_MSB(ctrl) ((ctrl)->cmd_base + 5u) +#define IDE_REG_LBA_HIGH(ctrl) ((ctrl)->cmd_base + 5u) +#define IDE_REG_DRIVEHEAD(ctrl) ((ctrl)->cmd_base + 6u) +#define IDE_REG_DEVICE(ctrl) ((ctrl)->cmd_base + 6u) +#define IDE_REG_STATUS(ctrl) ((ctrl)->cmd_base + 7u) +#define IDE_REG_COMMAND(ctrl) ((ctrl)->cmd_base + 7u) +#define IDE_REG_ALTSTATUS(ctrl) ((ctrl)->ctrl_base + 2u) +#define IDE_REG_DEVICE_CONTROL(ctrl) ((ctrl)->ctrl_base + 2u) + +struct ide_pio_command +{ + uint8_t feature; + uint8_t sector_count; + uint8_t lba_low; + uint8_t lba_mid; + uint8_t lba_high; + uint8_t device; +# define IDE_DH_DEFAULT (0xA0) +# define IDE_DH_HEAD(x) ((x) & 0x0F) +# define IDE_DH_MASTER (0x00) +# define IDE_DH_SLAVE (0x10) +# define IDE_DH_LBA (0x40) +# define IDE_DH_CHS (0x00) + uint8_t command; + uint8_t sector_count2; + uint8_t lba_low2; + uint8_t lba_mid2; + uint8_t lba_high2; +}; + +#define IDE_DEFAULT_COMMAND { 0xFFu, 0x01, 0x00, 0x0000, IDE_DH_DEFAULT } + +#define IDE_ERR_ICRC 0x80 /* ATA Ultra DMA bad CRC */ +#define IDE_ERR_BBK 0x80 /* ATA bad block */ +#define IDE_ERR_UNC 0x40 /* ATA uncorrected error */ +#define IDE_ERR_MC 0x20 /* ATA media change */ +#define IDE_ERR_IDNF 0x10 /* ATA id not found */ +#define IDE_ERR_MCR 0x08 /* ATA media change request */ +#define IDE_ERR_ABRT 0x04 /* ATA command aborted */ +#define IDE_ERR_NTK0 0x02 /* ATA track 0 not found */ +#define IDE_ERR_NDAM 0x01 /* ATA address mark not found */ + +#define IDE_STATUS_BSY 0x80 /* busy */ +#define IDE_STATUS_RDY 0x40 /* ready */ +#define IDE_STATUS_DF 0x20 /* device fault */ +#define IDE_STATUS_WFT 0x20 /* write fault (old name) */ +#define IDE_STATUS_SKC 0x10 /* seek complete */ +#define IDE_STATUS_DRQ 0x08 /* data request */ +#define IDE_STATUS_CORR 0x04 /* corrected */ +#define IDE_STATUS_IDX 0x02 /* index */ +#define IDE_STATUS_ERR 0x01 /* error (ATA) */ +#define IDE_STATUS_CHK 0x01 /* check (ATAPI) */ + +#define IDE_CTRL_HD15 0x08 /* bit should always be set to one */ +#define IDE_CTRL_SRST 0x04 /* soft reset */ +#define IDE_CTRL_NIEN 0x02 /* disable interrupts */ + + +/* Most mandtory and optional ATA commands (from ATA-3), */ + +#define IDE_CMD_CFA_ERASE_SECTORS 0xC0 +#define IDE_CMD_CFA_REQUEST_EXT_ERR_CODE 0x03 +#define IDE_CMD_CFA_TRANSLATE_SECTOR 0x87 +#define IDE_CMD_CFA_WRITE_MULTIPLE_WO_ERASE 0xCD +#define IDE_CMD_CFA_WRITE_SECTORS_WO_ERASE 0x38 +#define IDE_CMD_CHECK_POWER_MODE1 0xE5 +#define IDE_CMD_CHECK_POWER_MODE2 0x98 +#define IDE_CMD_DEVICE_RESET 0x08 +#define IDE_CMD_EXECUTE_DEVICE_DIAGNOSTIC 0x90 +#define IDE_CMD_FLUSH_CACHE 0xE7 +#define IDE_CMD_FORMAT_TRACK 0x50 +#define IDE_CMD_IDENTIFY_DEVICE 0xEC +#define IDE_CMD_IDENTIFY_DEVICE_PACKET 0xA1 +#define IDE_CMD_IDENTIFY_PACKET_DEVICE 0xA1 +#define IDE_CMD_IDLE1 0xE3 +#define IDE_CMD_IDLE2 0x97 +#define IDE_CMD_IDLE_IMMEDIATE1 0xE1 +#define IDE_CMD_IDLE_IMMEDIATE2 0x95 +#define IDE_CMD_INITIALIZE_DRIVE_PARAMETERS 0x91 +#define IDE_CMD_INITIALIZE_DEVICE_PARAMETERS 0x91 +#define IDE_CMD_NOP 0x00 +#define IDE_CMD_PACKET 0xA0 +#define IDE_CMD_READ_BUFFER 0xE4 +#define IDE_CMD_READ_DMA 0xC8 +#define IDE_CMD_READ_DMA_QUEUED 0xC7 +#define IDE_CMD_READ_MULTIPLE 0xC4 +#define IDE_CMD_READ_SECTORS 0x20 +#define IDE_CMD_READ_SECTORS_EXT 0x24 +#define IDE_CMD_READ_VERIFY_SECTORS 0x40 +#define IDE_CMD_RECALIBRATE 0x10 +#define IDE_CMD_SEEK 0x70 +#define IDE_CMD_SET_FEATURES 0xEF +#define IDE_CMD_SET_MAX_ADDR_EXT 0x24 +#define IDE_CMD_SET_MULTIPLE_MODE 0xC6 +#define IDE_CMD_SLEEP1 0xE6 +#define IDE_CMD_SLEEP2 0x99 +#define IDE_CMD_STANDBY1 0xE2 +#define IDE_CMD_STANDBY2 0x96 +#define IDE_CMD_STANDBY_IMMEDIATE1 0xE0 +#define IDE_CMD_STANDBY_IMMEDIATE2 0x94 +#define IDE_CMD_WRITE_BUFFER 0xE8 +#define IDE_CMD_WRITE_DMA 0xCA +#define IDE_CMD_WRITE_DMA_QUEUED 0xCC +#define IDE_CMD_WRITE_MULTIPLE 0xC5 +#define IDE_CMD_WRITE_SECTORS 0x30 +#define IDE_CMD_WRITE_VERIFY 0x3C + +/* IDE_CMD_SET_FEATURE sub commands */ +#define IDE_FEATURE_CFA_ENABLE_8BIT_PIO 0x01 +#define IDE_FEATURE_ENABLE_WRITE_CACHE 0x02 +#define IDE_FEATURE_SET_TRANSFER_MODE 0x03 +#define IDE_FEATURE_ENABLE_POWER_MANAGEMENT 0x05 +#define IDE_FEATURE_ENABLE_POWERUP_IN_STANDBY 0x06 +#define IDE_FEATURE_STANDBY_SPINUP_DRIVE 0x07 +#define IDE_FEATURE_CFA_ENABLE_POWER_MODE1 0x0A +#define IDE_FEATURE_DISABLE_MEDIA_STATUS_NOTIFICATION 0x31 +#define IDE_FEATURE_ENABLE_AUTOMATIC_ACOUSTIC_MANAGEMENT 0x42 +#define IDE_FEATURE_SET_MAXIMUM_HOST_INTERFACE_SECTOR_TIMES 0x43 +#define IDE_FEATURE_DISABLE_READ_LOOKAHEAD 0x55 +#define IDE_FEATURE_ENABLE_RELEASE_INTERRUPT 0x5D +#define IDE_FEATURE_ENABLE_SERVICE_INTERRUPT 0x5E +#define IDE_FEATURE_DISABLE_REVERTING_TO_POWERON_DEFAULTS 0x66 +#define IDE_FEATURE_CFA_DISABLE_8BIT_PIO 0x81 +#define IDE_FEATURE_DISABLE_WRITE_CACHE 0x82 +#define IDE_FEATURE_DISABLE_POWER_MANAGEMENT 0x85 +#define IDE_FEATURE_DISABLE_POWERUP_IN_STANDBY 0x86 +#define IDE_FEATURE_CFA_DISABLE_POWER_MODE1 0x8A +#define IDE_FEATURE_ENABLE_MEDIA_STATUS_NOTIFICATION 0x95 +#define IDE_FEATURE_ENABLE_READ_LOOKAHEAD 0xAA +#define IDE_FEATURE_DISABLE_AUTOMATIC_ACOUSTIC_MANAGEMENT 0xC2 +#define IDE_FEATURE_ENABLE_REVERTING_TO_POWERON_DEFAULTS 0xCC +#define IDE_FEATURE_DISABLE_SERVICE_INTERRUPT 0xDE + +static unsigned short ide_base[] = { + IDE_BASE0, + IDE_BASE1, + IDE_BASE2, + IDE_BASE3, + 0 +}; + +static struct controller controllers[IDE_MAX_CONTROLLERS]; +static struct harddisk_info harddisk_info[IDE_MAX_DRIVES]; + +static unsigned char ide_buffer[IDE_SECTOR_SIZE]; + +static int await_ide(int (*done)(struct controller *ctrl), + struct controller *ctrl, unsigned long timeout) +{ + int result; + for(;;) { + result = done(ctrl); + if (result) { + return 0; + } + //poll_interruptions(); + if ((timeout == 0) || (currticks() > timeout)) { + break; + } + } + printf("IDE time out\n"); + return -1; +} + +/* The maximum time any IDE command can last 31 seconds, + * So if any IDE commands takes this long we know we have problems. + */ +#define IDE_TIMEOUT (32*TICKS_PER_SEC) + +static int not_bsy(struct controller *ctrl) +{ + return !(inb(IDE_REG_STATUS(ctrl)) & IDE_STATUS_BSY); +} + +/* IDE drives assert BSY bit within 400 nsec when SRST is set. + * Use 2 msec since our tick is 1 msec */ +#define IDE_RESET_PULSE (2*TICKS_PER_SEC / 18) + +static int bsy(struct controller *ctrl) +{ + return inb(IDE_REG_STATUS(ctrl)) & IDE_STATUS_BSY; +} + +#if !BSY_SET_DURING_SPINUP +static int timeout(struct controller *ctrl) +{ + return 0; +} +#endif + +static void print_status(struct controller *ctrl) +{ + debug("IDE: status=%#x, err=%#x\n", + inb(IDE_REG_STATUS(ctrl)), inb(IDE_REG_ERROR(ctrl))); +} + +static int ide_software_reset(struct controller *ctrl) +{ + /* Wait a little bit in case this is immediately after + * hardware reset. + */ + mdelay(2); + /* A software reset should not be delivered while the bsy bit + * is set. If the bsy bit does not clear in a reasonable + * amount of time give up. + */ + debug("Waiting for ide%d to become ready for reset... ", + ctrl - controllers); + if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) { + debug("failed\n"); + return -1; + } + debug("ok\n"); + + /* Disable Interrupts and reset the ide bus */ + outb(IDE_CTRL_HD15 | IDE_CTRL_SRST | IDE_CTRL_NIEN, + IDE_REG_DEVICE_CONTROL(ctrl)); + /* If BSY bit is not asserted within 400ns, no device there */ + if (await_ide(bsy, ctrl, currticks() + IDE_RESET_PULSE) < 0) { + return -1; + } + outb(IDE_CTRL_HD15 | IDE_CTRL_NIEN, IDE_REG_DEVICE_CONTROL(ctrl)); + mdelay(2); + if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } + return 0; +} + +static void pio_set_registers( + struct controller *ctrl, const struct ide_pio_command *cmd) +{ + uint8_t device; + /* Disable Interrupts */ + outb(IDE_CTRL_HD15 | IDE_CTRL_NIEN, IDE_REG_DEVICE_CONTROL(ctrl)); + + /* Possibly switch selected device */ + device = inb(IDE_REG_DEVICE(ctrl)); + outb(cmd->device, IDE_REG_DEVICE(ctrl)); + if ((device & (1UL << 4)) != (cmd->device & (1UL << 4))) { + /* Allow time for the selected drive to switch, + * The linux ide code suggests 50ms is the right + * amount of time to use here. + */ + mdelay(50); + } + outb(cmd->feature, IDE_REG_FEATURE(ctrl)); + if (cmd->command == IDE_CMD_READ_SECTORS_EXT) { + outb(cmd->sector_count2, IDE_REG_SECTOR_COUNT(ctrl)); + outb(cmd->lba_low2, IDE_REG_LBA_LOW(ctrl)); + outb(cmd->lba_mid2, IDE_REG_LBA_MID(ctrl)); + outb(cmd->lba_high2, IDE_REG_LBA_HIGH(ctrl)); + } + outb(cmd->sector_count, IDE_REG_SECTOR_COUNT(ctrl)); + outb(cmd->lba_low, IDE_REG_LBA_LOW(ctrl)); + outb(cmd->lba_mid, IDE_REG_LBA_MID(ctrl)); + outb(cmd->lba_high, IDE_REG_LBA_HIGH(ctrl)); + outb(cmd->command, IDE_REG_COMMAND(ctrl)); +} + + +static int pio_non_data(struct controller *ctrl, const struct ide_pio_command *cmd) +{ + /* Wait until the busy bit is clear */ + if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } + + pio_set_registers(ctrl, cmd); + ndelay(400); + if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } + /* FIXME is there more error checking I could do here? */ + return 0; +} + +static int pio_data_in(struct controller *ctrl, const struct ide_pio_command *cmd, + void *buffer, size_t bytes) +{ + unsigned int status; + + /* FIXME handle commands with multiple blocks */ + /* Wait until the busy bit is clear */ + if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } + + /* How do I tell if INTRQ is asserted? */ + pio_set_registers(ctrl, cmd); + ndelay(400); + if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } + status = inb(IDE_REG_STATUS(ctrl)); + if (!(status & IDE_STATUS_DRQ)) { + print_status(ctrl); + return -1; + } + insw(IDE_REG_DATA(ctrl), buffer, bytes/2); + status = inb(IDE_REG_STATUS(ctrl)); + if (status & IDE_STATUS_DRQ) { + print_status(ctrl); + return -1; + } + return 0; +} + +static int pio_packet(struct harddisk_info *info, int in, + const void *packet, int packet_len, + void *buffer, int buffer_len) +{ + unsigned int status; + struct ide_pio_command cmd; + + memset(&cmd, 0, sizeof(cmd)); + + /* Wait until the busy bit is clear */ + if (await_ide(not_bsy, info->ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } + + /* Issue a PACKET command */ + cmd.lba_mid = (uint8_t) buffer_len; + cmd.lba_high = (uint8_t) (buffer_len >> 8); + cmd.device = IDE_DH_DEFAULT | info->slave; + cmd.command = IDE_CMD_PACKET; + pio_set_registers(info->ctrl, &cmd); + ndelay(400); + if (await_ide(not_bsy, info->ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } + status = inb(IDE_REG_STATUS(info->ctrl)); + if (!(status & IDE_STATUS_DRQ)) { + debug("no drq after PACKET\n"); + print_status(info->ctrl); + return -1; + } + + /* Send the packet */ + outsw(IDE_REG_DATA(info->ctrl), packet, packet_len/2); + + if (await_ide(not_bsy, info->ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } + status = inb(IDE_REG_STATUS(info->ctrl)); + if (buffer_len == 0) { + if (status & IDE_STATUS_DRQ) { + debug("drq after non-data command\n"); + print_status(info->ctrl); + return -1; + } + return 0; + } + + if (!(status & IDE_STATUS_DRQ)) { + debug("no drq after sending packet\n"); + print_status(info->ctrl); + return -1; + } + + insw(IDE_REG_DATA(info->ctrl), buffer, buffer_len/2); + + status = inb(IDE_REG_STATUS(info->ctrl)); + if (status & IDE_STATUS_DRQ) { + debug("drq after insw\n"); + print_status(info->ctrl); + return -1; + } + return 0; +} + +static inline int ide_read_sector_chs( + struct harddisk_info *info, void *buffer, unsigned long sector) +{ + struct ide_pio_command cmd; + unsigned int track; + unsigned int offset; + unsigned int cylinder; + + memset(&cmd, 0, sizeof(cmd)); + cmd.sector_count = 1; + + //debug("ide_read_sector_chs: sector= %ld.\n",sector); + + track = sector / info->sectors_per_track; + /* Sector number */ + offset = 1 + (sector % info->sectors_per_track); + cylinder = track / info->heads; + cmd.lba_low = offset; + cmd.lba_mid = cylinder & 0xff; + cmd.lba_high = (cylinder >> 8) & 0xff; + cmd.device = IDE_DH_DEFAULT | + IDE_DH_HEAD(track % info->heads) | + info->slave | + IDE_DH_CHS; + cmd.command = IDE_CMD_READ_SECTORS; + return pio_data_in(info->ctrl, &cmd, buffer, IDE_SECTOR_SIZE); +} + +static inline int ide_read_sector_lba( + struct harddisk_info *info, void *buffer, unsigned long sector) +{ + struct ide_pio_command cmd; + memset(&cmd, 0, sizeof(cmd)); + + cmd.sector_count = 1; + cmd.lba_low = sector & 0xff; + cmd.lba_mid = (sector >> 8) & 0xff; + cmd.lba_high = (sector >> 16) & 0xff; + cmd.device = IDE_DH_DEFAULT | + ((sector >> 24) & 0x0f) | + info->slave | + IDE_DH_LBA; + cmd.command = IDE_CMD_READ_SECTORS; + //debug("%s: sector= %ld, device command= 0x%x.\n",__FUNCTION__,(unsigned long) sector, cmd.device); + return pio_data_in(info->ctrl, &cmd, buffer, IDE_SECTOR_SIZE); +} + +static inline int ide_read_sector_lba48( + struct harddisk_info *info, void *buffer, sector_t sector) +{ + struct ide_pio_command cmd; + memset(&cmd, 0, sizeof(cmd)); + //debug("ide_read_sector_lba48: sector= %ld.\n",(unsigned long) sector); + + cmd.sector_count = 1; + cmd.lba_low = sector & 0xff; + cmd.lba_mid = (sector >> 8) & 0xff; + cmd.lba_high = (sector >> 16) & 0xff; + cmd.lba_low2 = (sector >> 24) & 0xff; + cmd.lba_mid2 = (sector >> 32) & 0xff; + cmd.lba_high2 = (sector >> 40) & 0xff; + cmd.device = info->slave | IDE_DH_LBA; + cmd.command = IDE_CMD_READ_SECTORS_EXT; + return pio_data_in(info->ctrl, &cmd, buffer, IDE_SECTOR_SIZE); +} + +static inline int ide_read_sector_packet( + struct harddisk_info *info, void *buffer, sector_t sector) +{ + char packet[12]; + static uint8_t cdbuffer[CDROM_SECTOR_SIZE]; + static struct harddisk_info *last_disk = 0; + static sector_t last_sector = (sector_t) -1; + uint8_t *buf; + uint32_t hw_sector; + + //debug("sector=%Ld\n", sector); + + if (info->hw_sector_size == CDROM_SECTOR_SIZE) { + buf = cdbuffer; + hw_sector = sector >> 2; + } else { + buf = buffer; + hw_sector = sector; + } + + if (buf==buffer || info != last_disk || hw_sector != last_sector) { + //debug("hw_sector=%u\n", hw_sector); + memset(packet, 0, sizeof packet); + packet[0] = 0x28; /* READ */ + packet[2] = hw_sector >> 24; + packet[3] = hw_sector >> 16; + packet[4] = hw_sector >> 8; + packet[5] = hw_sector >> 0; + packet[7] = 0; + packet[8] = 1; /* length */ + + if (pio_packet(info, 1, packet, sizeof packet, + buf, info->hw_sector_size) != 0) { + debug("read error\n"); + return -1; + } + last_disk = info; + last_sector = hw_sector; + } + + if (buf != buffer) + memcpy(buffer, &cdbuffer[(sector & 3) << 9], IDE_SECTOR_SIZE); + return 0; +} + +int ide_read(int drive, sector_t sector, void *buffer) +{ + struct harddisk_info *info = &harddisk_info[drive]; + int result; + + //debug("drive=%d, sector=%ld\n",drive,(unsigned long) sector); + /* Report the buffer is empty */ + if (sector > info->sectors) { + return -1; + } + if (info->address_mode == ADDRESS_MODE_CHS) { + result = ide_read_sector_chs(info, buffer, sector); + } + else if (info->address_mode == ADDRESS_MODE_LBA) { + result = ide_read_sector_lba(info, buffer, sector); + } + else if (info->address_mode == ADDRESS_MODE_LBA48) { + result = ide_read_sector_lba48(info, buffer, sector); + } + else if (info->address_mode == ADDRESS_MODE_PACKET) { + result = ide_read_sector_packet(info, buffer, sector); + } + else { + result = -1; + } + return result; +} + +static int init_drive_x(struct harddisk_info *info, struct controller *ctrl, + int slave, int drive, unsigned char *buffer, int ident_command) +{ + uint16_t* drive_info; + struct ide_pio_command cmd; + int i; + + + info->ctrl = ctrl; + info->heads = 0u; + info->cylinders = 0u; + info->sectors_per_track = 0u; + info->address_mode = IDE_DH_CHS; + info->sectors = 0ul; + info->drive_exists = 0; + info->slave_absent = 0; + info->removable = 0; + info->hw_sector_size = IDE_SECTOR_SIZE; + info->slave = slave?IDE_DH_SLAVE: IDE_DH_MASTER; + + debug("Testing for hd%c\n", 'a'+drive); + + /* Select the drive that we are testing */ + outb(IDE_DH_DEFAULT | IDE_DH_HEAD(0) | IDE_DH_CHS | info->slave, + IDE_REG_DEVICE(ctrl)); + mdelay(50); + + /* Test to see if the drive registers exist, + * In many cases this quickly rules out a missing drive. + */ + for(i = 0; i < 4; i++) { + outb(0xaa + i, (ctrl->cmd_base) + 2 + i); + } + for(i = 0; i < 4; i++) { + if (inb((ctrl->cmd_base) + 2 + i) != 0xaa + i) { + return 1; + } + } + for(i = 0; i < 4; i++) { + outb(0x55 + i, (ctrl->cmd_base) + 2 + i); + } + for(i = 0; i < 4; i++) { + if (inb((ctrl->cmd_base) + 2 + i) != 0x55 + i) { + return 1; + } + } + debug("Probing for hd%c\n", 'a'+drive); + + memset(&cmd, 0, sizeof(cmd)); + cmd.device = IDE_DH_DEFAULT | IDE_DH_HEAD(0) | IDE_DH_CHS | info->slave; + cmd.command = ident_command; + + + if (pio_data_in(ctrl, &cmd, buffer, IDE_SECTOR_SIZE) < 0) { + /* Well, if that command didn't work, we probably don't have drive. */ + return 1; + } + + /* Now suck the data out */ + drive_info = (uint16_t *)buffer; + if (drive_info[2] == 0x37C8) { + /* If the response is incomplete spin up the drive... */ + memset(&cmd, 0, sizeof(cmd)); + cmd.device = IDE_DH_DEFAULT | IDE_DH_HEAD(0) | IDE_DH_CHS | + info->slave; + cmd.feature = IDE_FEATURE_STANDBY_SPINUP_DRIVE; + if (pio_non_data(ctrl, &cmd) < 0) { + /* If the command doesn't work give up on the drive */ + return 1; + } + + } + if ((drive_info[2] == 0x37C8) || (drive_info[2] == 0x8C73)) { + /* The response is incomplete retry the drive info command */ + memset(&cmd, 0, sizeof(cmd)); + cmd.device = IDE_DH_DEFAULT | IDE_DH_HEAD(0) | IDE_DH_CHS | + info->slave; + cmd.command = ident_command; + if(pio_data_in(ctrl, &cmd, buffer, IDE_SECTOR_SIZE) < 0) { + /* If the command didn't work give up on the drive. */ + return 1; + } + } + if ((drive_info[2] != 0x37C8) && + (drive_info[2] != 0x738C) && + (drive_info[2] != 0x8C73) && + (drive_info[2] != 0xC837) && + (drive_info[2] != 0x0000)) { + debugx("Invalid IDE Configuration: %hx\n", drive_info[2]); + return 1; + } + for(i = 27; i < 47; i++) { + info->model_number[((i-27)<< 1)] = (drive_info[i] >> 8) & 0xff; + info->model_number[((i-27)<< 1)+1] = drive_info[i] & 0xff; + } + info->model_number[40] = '\0'; + info->drive_exists = 1; + + /* See if LBA is supported */ + if (ident_command == IDE_CMD_IDENTIFY_PACKET_DEVICE) { + info->address_mode = ADDRESS_MODE_PACKET; + info->removable = 1; /* XXX */ + } else if (drive_info[49] & (1 << 9)) { + info->address_mode = ADDRESS_MODE_LBA; + info->sectors = (drive_info[61] << 16) | (drive_info[60]); +// debug("LBA mode, sectors=%Ld\n", info->sectors); + /* Enable LBA48 mode if it is present */ + if (drive_info[83] & (1 <<10)) { + /* Should LBA48 depend on LBA? */ + info->address_mode = ADDRESS_MODE_LBA48; + info->sectors = + (((sector_t)drive_info[103]) << 48) | + (((sector_t)drive_info[102]) << 32) | + (((sector_t)drive_info[101]) << 16) | + (((sector_t)drive_info[100]) << 0); +// debug("LBA48 mode, sectors=%Ld\n", info->sectors); + } + } else { + info->address_mode = ADDRESS_MODE_CHS; + info->heads = drive_info[3]; + info->cylinders = drive_info[1]; + info->sectors_per_track = drive_info[6]; + info->sectors = + info->sectors_per_track * + info->heads * + info->cylinders; + debug("CHS mode, sectors_per_track=[%d], heads=[%d], cylinders=[%d]\n", + info->sectors_per_track, + info->heads, + info->cylinders); +// debug("sectors=%Ld\n", info->sectors); + } + /* See if we have a slave */ + if (!info->slave && (((drive_info[93] >> 14) & 3) == 1)) { + info->slave_absent = !(drive_info[93] & (1 << 5)); + } + + /* See if we need to put the device in CFA power mode 1 */ + if ((drive_info[160] & ((1 << 15) | (1 << 13)| (1 << 12))) == + ((1 << 15) | (1 << 13)| (1 << 12))) { + memset(&cmd, 0, sizeof(cmd)); + cmd.device = IDE_DH_DEFAULT | IDE_DH_HEAD(0) | IDE_DH_CHS | info->slave; + cmd.feature = IDE_FEATURE_CFA_ENABLE_POWER_MODE1; + if (pio_non_data(ctrl, &cmd) < 0) { + /* If I need to power up the drive, and I can't + * give up. + */ + debugx("Cannot power up CFA device\n"); + return 1; + } + } + + /* Some extra steps for older drives.. */ + if (info->address_mode != ADDRESS_MODE_PACKET) { + /* Initialize drive parameters + * This is an obsolete command (disappeared as of ATA-6) + * but old drives need it before accessing media. */ + memset(&cmd, 0, sizeof(cmd)); + cmd.device = IDE_DH_DEFAULT | IDE_DH_HEAD(drive_info[3] - 1) + | info->slave; + cmd.sector_count = drive_info[6]; + cmd.command = IDE_CMD_INITIALIZE_DRIVE_PARAMETERS; + debug("Init device params... "); + if (pio_non_data(ctrl, &cmd) < 0) { + debug("failed (ok for newer drives)\n"); + } + else{ + debug("ok\n"); + } + } + + printf("hd%c: %s", + 'a'+drive, + (info->address_mode==ADDRESS_MODE_CHS) ? "CHS" : + (info->address_mode==ADDRESS_MODE_LBA) ? "LBA" : + (info->address_mode==ADDRESS_MODE_LBA48) ? "LBA48" : + (info->address_mode==ADDRESS_MODE_PACKET) ? "ATAPI" : "???"); +#if 0 +// can not pass compiler + if (info->sectors > (10LL*1000*1000*1000/512)) + printf(" %uGB", (unsigned) (info->sectors / (1000*1000*1000/512))); + else if (info->sectors > (10*1000*1000/512)) + printf(" %uMB", (unsigned) (info->sectors / (1000*1000/512))); + else if (info->sectors > 0) + printf(" %uKB", (unsigned) (info->sectors / 2)); +#endif + printf(": %s\n", info->model_number); + return 0; +} + +/* Experimental floating bus detection + * As Eric mentions, we get stuck when the bus has no drive + * and floating high. To avoid this, try some heuristics. + * This is based on a paper on Phoenix website. --ts1 */ +static int ide_bus_floating(struct controller *ctrl) +{ + unsigned long timeout; + unsigned char status; + + /* Test 1: if status reads 0xff, probably no device is present + * on the bus. Repeat this for 20msec. */ + timeout = currticks() + 20 * TICKS_PER_SEC / 18; + status = 0; + do { + /* Take logical OR to avoid chattering */ + status |= inb(IDE_REG_STATUS(ctrl)); + /* If it makes 0xff, it's possible to be floating, + * do test2 to ensure. */ + if (status == 0xff) + goto test2; + /* If BSY bit is not set, it's harmless to continue probing. */ + if ((status & IDE_STATUS_BSY) == 0) + return 0; + } while (currticks() < timeout); + /* Timed out. Logical ORed status didn't make 0xFF. + * We have something there. */ + return 0; + +test2: + /* Test 2: write something to registers, then read back and + * compare. Note that ATA spec inhibits this while BSY is set, + * but for many drives this works. This is a confirmation step anyway. + */ + outb(0xaa, ctrl->cmd_base + 2); + outb(0x55, ctrl->cmd_base + 3); + outb(0xff, ctrl->cmd_base + 4); + if (inb(ctrl->cmd_base+2) == 0xaa + && inb(ctrl->cmd_base+3) == 0x55 + && inb(ctrl->cmd_base+4) == 0xff) { + /* We have some registers there. + * Though this does not mean it is not a NIC or something... */ + return 0; + } + + /* Status port is 0xFF, and other registers are not there. + * Most certainly this bus is floating. */ + debugx("Detected floating bus\n"); + return 1; +} + +static int init_controller(struct controller *ctrl, int drive, unsigned char *buffer) +{ + struct harddisk_info *info; + + /* Put the drives ide channel in a know state and wait + * for the drives to spinup. + * + * In practice IDE disks tend not to respond to commands until + * they have spun up. This makes IDE hard to deal with + * immediately after power up, as the delays can be quite + * long, so we must be very careful here. + * + * There are two pathological cases that must be dealt with: + * + * - The BSY bit not being set while the IDE drives spin up. + * In this cases only a hard coded delay will work. As + * I have not reproduced it, and this is out of spec for + * IDE drives the work around can be enabled by setting + * BSY_SET_DURING_SPINUP to 0. + * + * - The BSY bit floats high when no drives are plugged in. + * This case will not be detected except by timing out but + * we avoid the problems by only probing devices we are + * supposed to boot from. If we don't do the probe we + * will not experience the problem. + * + * So speed wise I am only slow if the BSY bit is not set + * or not reported by the IDE controller during spinup, which + * is quite rare. + * + */ +#if !BSY_SET_DURING_SPINUP + if (await_ide(timeout, ctrl, currticks() + IDE_TIMEOUT) < 0) { + return -1; + } +#endif + /* ts1: Try some heuristics to avoid waiting for floating bus */ + if (ide_bus_floating(ctrl)) + return -1; + + if (ide_software_reset(ctrl) < 0) { + return -1; + } + + /* Note: I have just done a software reset. It may be + * reasonable to just read the boot time signatures + * off of the drives to see if they are present. + * + * For now I will go with just sending commands to the drives + * and assuming filtering out missing drives by detecting registers + * that won't set and commands that fail to execute properly. + */ + + /* Now initialize the individual drives */ + info = &harddisk_info[drive]; + init_drive_x(info, ctrl, 0, drive, buffer, IDE_CMD_IDENTIFY_DEVICE); + if (!info->drive_exists) + init_drive_x(info, ctrl, 0, drive, buffer, + IDE_CMD_IDENTIFY_PACKET_DEVICE); + if (info->drive_exists && !info->slave_absent) { + drive++; + info++; + init_drive_x(info, ctrl, 1, drive, buffer, + IDE_CMD_IDENTIFY_DEVICE); + if (!info->drive_exists) + init_drive_x(info, ctrl, 1, drive, buffer, + IDE_CMD_IDENTIFY_PACKET_DEVICE); + } + + return 0; +} + +static int +atapi_request_sense(struct harddisk_info *info, uint8_t *asc, uint8_t *ascq) +{ + uint8_t packet[12]; + uint8_t buf[18]; + int i; + + memset(packet, 0, sizeof packet); + packet[0] = 0x03; /* REQUEST SENSE */ + packet[4] = sizeof buf; + if (pio_packet(info, 1, packet, sizeof packet, buf, sizeof buf) != 0) + return -1; + + for (i = 0; i < sizeof buf; i++) + debug("%02x ", buf[i]); + debug("\n"); + + if (asc) + *asc = buf[12]; + if (ascq) + *ascq = buf[13]; + return 0; +} + +static int atapi_detect_medium(struct harddisk_info *info) +{ + uint8_t packet[12]; + uint8_t buf[8]; + uint32_t block_len, sectors; + unsigned long timeout; + uint8_t asc, ascq; + int in_progress; + + memset(packet, 0, sizeof packet); + packet[0] = 0x25; /* READ CAPACITY */ + + /* Retry READ CAPACITY for 5 seconds unless MEDIUM NOT PRESENT + * is reported by the drive. If the drive reports "IN PROGRESS", + * 30 seconds is added. */ + timeout = currticks() + 5*TICKS_PER_SEC; + in_progress = 0; + while (currticks() < timeout) { + if (pio_packet(info, 1, packet, sizeof packet, buf, sizeof buf) + == 0) + goto ok; + + if (atapi_request_sense(info, &asc, &ascq) == 0) { + if (asc == 0x3a) { /* MEDIUM NOT PRESENT */ + debug("Device reports MEDIUM NOT PRESENT\n"); + return -1; + } + + if (asc == 0x04 && ascq == 0x01 && !in_progress) { + /* IN PROGRESS OF BECOMING READY */ + printf("Waiting for drive to detect " + "the medium... "); + /* Allow 30 seconds more */ + timeout = currticks() + 30*TICKS_PER_SEC; + in_progress = 1; + } + } + + mdelay(100); + } + debug("read capacity failed\n"); + return -1; +ok: + + block_len = (uint32_t) buf[4] << 24 + | (uint32_t) buf[5] << 16 + | (uint32_t) buf[6] << 8 + | (uint32_t) buf[7] << 0; + debug("block_len=%u\n", block_len); + if (block_len != IDE_SECTOR_SIZE && block_len != CDROM_SECTOR_SIZE) { + debugx("Unsupported sector size %u\n", block_len); + return -1; + } + info->hw_sector_size = block_len; + + sectors = (uint32_t) buf[0] << 24 + | (uint32_t) buf[1] << 16 + | (uint32_t) buf[2] << 8 + | (uint32_t) buf[3] << 0; + + debug("sectors=%u\n", sectors); + if (info->hw_sector_size == CDROM_SECTOR_SIZE) + sectors <<= 2; /* # of sectors in 512-byte "soft" sector */ + if (sectors != info->sectors) + printf("%dMB medium detected\n", sectors>>(20-9)); + info->sectors = sectors; + return 0; +} + +static int detect_medium(struct harddisk_info *info) +{ + if (info->address_mode == ADDRESS_MODE_PACKET) { + if (atapi_detect_medium(info) != 0) + return -1; + } else { + debug("not implemented for non-ATAPI device\n"); + return -1; + } + return 0; +} + +static int find_ide_controller_compat(struct controller *ctrl, int index) +{ + if (index >= IDE_MAX_CONTROLLERS) + return -1; + ctrl->cmd_base = ide_base[index]; + ctrl->ctrl_base = ide_base[index] + IDE_REG_EXTENDED_OFFSET; + return 0; +} + +#ifdef SUPPORT_PCI +static int find_ide_controller(struct controller *ctrl, int ctrl_index) +{ + int pci_index; + struct pci_device *dev; + unsigned int mask; + uint32_t x; + + /* A PCI IDE controller has two channels (pri, sec) */ + pci_index = ctrl_index >> 1; + + /* Find a IDE storage class device */ + dev = pci_find_device_2(-1, -1, 0x0101, 0x0180, -1, pci_index); + if (!dev) { + debug("PCI IDE #%d not found\n", pci_index); + return -1; + } + + debug("found PCI IDE controller %04x:%04x prog_if=%#x\n", + dev->vendor, dev->dev_id, ((dev->class>>8) & 0xff)); + + /* See how this controller is configured */ + mask = (ctrl_index & 1) ? 4 : 1; + debug("%s channel: ", (ctrl_index & 1) ? "secodary" : "primary"); + if ( (((dev->class>>8) & 0xff) & mask) || ((dev->class>>16)!= 0x0101)) { // 0x0180 and other must use native PCI mode + debug("native PCI mode\n"); + if ((ctrl_index & 1) == 0) { + + /* Primary channel */ + pci_read_config_dword(dev, PCI_BASE_ADDRESS_0,&x); + ctrl->cmd_base = x; + pci_read_config_dword(dev, PCI_BASE_ADDRESS_1,&x); + ctrl->ctrl_base = x; + } else { + /* Secondary channel */ + pci_read_config_dword(dev, PCI_BASE_ADDRESS_2,&x); + ctrl->cmd_base = x; + pci_read_config_dword(dev, PCI_BASE_ADDRESS_3,&x); + ctrl->ctrl_base = x; + } + ctrl->cmd_base &= ~3; + ctrl->ctrl_base &= ~3; + } else { + debug("compatibility mode\n"); + if (find_ide_controller_compat(ctrl, ctrl_index) != 0) + return -1; + } + + debug("cmd_base=%#x ctrl_base=%#x\n", ctrl->cmd_base, ctrl->ctrl_base); +#if 0 + debug("cmd+0=%0#x\n", inb(ctrl->cmd_base+0)); + debug("cmd+1=%0#x\n", inb(ctrl->cmd_base+1)); + debug("cmd+2=%0#x\n", inb(ctrl->cmd_base+2)); + debug("cmd+3=%0#x\n", inb(ctrl->cmd_base+3)); + debug("cmd+4=%0#x\n", inb(ctrl->cmd_base+4)); + debug("cmd+5=%0#x\n", inb(ctrl->cmd_base+5)); + debug("cmd+6=%0#x\n", inb(ctrl->cmd_base+6)); + debug("cmd+7=%0#x\n", inb(ctrl->cmd_base+7)); + debug("ctrl+0=%0#x\n", inb(ctrl->ctrl_base+0)); + debug("ctrl+1=%0#x\n", inb(ctrl->ctrl_base+1)); + debug("ctrl+2=%0#x\n", inb(ctrl->ctrl_base+2)); + debug("ctrl+3=%0#x\n", inb(ctrl->ctrl_base+3)); +#endif + return 0; +} +#else /* !SUPPORT_PCI */ +# define find_ide_controller find_ide_controller_compat +#endif + +int ide_probe(int drive) +{ + struct controller *ctrl; + int ctrl_index; + struct harddisk_info *info; + + if (drive >= IDE_MAX_DRIVES) { + debugx("Unsupported drive number\n"); + return -1; + } + + /* A controller has two drives (master, slave) */ + ctrl_index = drive >> 1; + + ctrl = &controllers[ctrl_index]; + if (ctrl->cmd_base == 0) { + if (find_ide_controller(ctrl, ctrl_index) != 0) { + debugx("IDE channel %d not found\n", ctrl_index); + return -1; + } + if (init_controller(ctrl, drive & ~1, ide_buffer) != 0) { + printf("No drive detected on IDE channel %d\n", + ctrl_index); + return -1; + } + } + info = &harddisk_info[drive]; + if (!info->drive_exists) { + printf("Drive %d does not exist\n", drive); + return -1; + } + + if (info->removable) { + if (detect_medium(info) != 0) { + printf("Media detection failed\n"); + return -1; + } + } + + return 0; +} + +/* vim:set sts=8 sw=8: */ diff --git a/src/filo/fs/blockdev.c b/src/filo/fs/blockdev.c new file mode 100644 index 00000000..f159ff2f --- /dev/null +++ b/src/filo/fs/blockdev.c @@ -0,0 +1,383 @@ +#include + +#include +#include + +#define DEBUG_THIS DEBUG_BLOCKDEV +#include + +#define NUM_CACHE 64 +static unsigned char buf_cache[NUM_CACHE][512]; +static unsigned long cache_sect[NUM_CACHE]; + +static char dev_name[256]; + +int dev_type = -1; +int dev_drive = -1; +unsigned long part_start; +unsigned long part_length; +int using_devsize; + +static inline int has_pc_part_magic(unsigned char *sect) +{ + return sect[510]==0x55 && sect[511]==0xAA; +} + +static inline int is_pc_extended_part(unsigned char type) +{ + return type==5 || type==0xf || type==0x85; +} + +/* IBM-PC/MS-DOS style partitioning scheme */ +static int open_pc_partition(int part, unsigned long *start_p, + unsigned long *length_p) +{ + /* Layout of PC partition table */ + struct pc_partition { + unsigned char boot; + unsigned char head; + unsigned char sector; + unsigned char cyl; + unsigned char type; + unsigned char e_head; + unsigned char e_sector; + unsigned char e_cyl; + unsigned char start_sect[4]; /* unaligned little endian */ + unsigned char nr_sects[4]; /* ditto */ + } *p; + unsigned char buf[512]; + + /* PC partition probe */ + if (!devread(0, 0, sizeof(buf), buf)) { + debug("device read failed\n"); + return 0; + } + if (!has_pc_part_magic(buf)) { + debug("pc partition magic number not found\n"); + //debug_hexdump(buf, 512); + return PARTITION_UNKNOWN; + } + p = (struct pc_partition *) (buf + 0x1be); + if (part < 4) { + /* Primary partition */ + p += part; + if (p->type==0 || is_pc_extended_part(p->type)) { + printf("Partition %d does not exist\n", part+1); + return 0; + } + *start_p = get_le32(p->start_sect); + *length_p = get_le32(p->nr_sects); + return 1; + } else { + /* Extended partition */ + int i; + int cur_part; + unsigned long ext_start, cur_table; + /* Search for the extended partition + * which contains logical partitions */ + for (i = 0; i < 4; i++) { + if (is_pc_extended_part(p[i].type)) + break; + } + if (i >= 4) { + printf("Extended partition not found\n"); + return 0; + } + debug("Extended partition at %d\n", i+1); + /* Visit each logical partition labels */ + ext_start = get_le32(p[i].start_sect); + cur_table = ext_start; + cur_part = 4; + for (;;) { + debug("cur_part=%d at %lu\n", cur_part, cur_table); + if (!devread(cur_table, 0, sizeof(buf), buf)) + return 0; + if (!has_pc_part_magic(buf)) { + debug("no magic\n"); + break; + } + + p = (struct pc_partition *) (buf + 0x1be); + /* First entry is the logical partition */ + if (cur_part == part) { + if (p->type==0) { + printf("Partition %d is empty\n", part+1); + return 0; + } + *start_p = cur_table + get_le32(p->start_sect); + *length_p = get_le32(p->nr_sects); + return 1; + } + /* Second entry is link to next partition */ + if (!is_pc_extended_part(p[1].type)) { + debug("no link\n"); + break; + } + cur_table = ext_start + get_le32(p[1].start_sect); + + cur_part++; + } + printf("Logical partition %d not exist\n", part+1); + return 0; + } +} + +static void flush_cache(void) +{ + int i; + for (i = 0; i < NUM_CACHE; i++) + cache_sect[i] = (unsigned long) -1; +} + +static int parse_device_name(const char *name, int *type, int *drive, + int *part, uint64_t *offset, uint64_t *length) +{ + *offset = *length = 0; + + if (memcmp(name, "hd", 2) == 0) { + *type = DISK_IDE; + name += 2; + if (*name < 'a' || *name > 'z') { + printf("Invalid drive\n"); + return 0; + } + *drive = *name - 'a'; + name++; + } else if (memcmp(name, "mem", 3) == 0) { + *type = DISK_MEM; + name += 3; + *drive = 0; + } else if (memcmp(name, "ud", 2) == 0) { + *type = DISK_USB; + name += 2; + if (*name < 'a' || *name > 'z') { + printf("Invalid drive\n"); + return 0; + } + *drive = *name - 'a'; + name++; + } else { + printf("Unknown device type\n"); + return 0; + } + + *part = (int) simple_strtoull(name, (char **)&name, 0); + + if (*name == '@') { + name++; + *offset = strtoull_with_suffix(name, (char **)&name, 0); + if (*name == ',') + *length = strtoull_with_suffix(name+1, (char **)&name, 0); +// debug("offset=%#Lx length=%#Lx\n", *offset, *length); + } + + if (*name != '\0') { + printf("Can't parse device name\n"); + return 0; + } + + return 1; +} + +int devopen(const char *name, int *reopen) +{ + int type, drive, part; + uint64_t offset, length; + uint32_t disk_size = 0; + + /* Don't re-open the device that's already open */ + if (strcmp(name, dev_name) == 0) { + debug("already open\n"); + *reopen = 1; + return 1; + } + *reopen = 0; + + if (!parse_device_name(name, &type, &drive, &part, &offset, &length)) { + debug("failed to parse device name: %s\n", name); + return 0; + } + + /* Do simple sanity check first */ + if (offset & 0x1ff) { + printf("Device offset must be multiple of 512\n"); + return 0; + } + if (length & 0x1ff) { + debugx("WARNING: length is rounded up to multiple of 512\n"); + length = (length + 0x1ff) & ~0x1ff; + } + + switch (type) { +#ifdef IDE_DISK + case DISK_IDE: + if (ide_probe(drive) != 0) { + debug("failed to open ide\n"); + return 0; + } + disk_size = (uint32_t) -1; /* FIXME */ + break; +#endif + case DISK_MEM: + disk_size = 1 << (32 - 9); /* 4GB/512-byte */ + break; +#ifdef USB_DISK + case DISK_USB: + if (usb_probe(drive) != 0) { + debug("failed to open usb\n"); + return 0; + } + disk_size = (uint32_t) -1; /* FIXME */ + break; +#endif + default: + printf("Unknown device type %d\n", type); + return 0; + } + + if (dev_type != type || dev_drive != drive) + flush_cache(); + + /* start with whole disk */ + dev_type = type; + dev_drive = drive; + part_start = 0; + part_length = disk_size; + using_devsize = 1; + + if (part != 0) { + /* partition is specified */ + int ret; + ret = open_pc_partition(part - 1, &part_start, &part_length); + if (ret == PARTITION_UNKNOWN) { + ret = open_eltorito_image(part - 1, &part_start, &part_length); + if (ret == PARTITION_UNKNOWN) { + printf("Unrecognized partitioning scheme\n"); + return 0; + } + } + if (ret == 0) { + debug("can't open partition %d\n", part); + return 0; + } + + debug("Partition %d start %lu length %lu\n", part, + part_start, part_length); + } + + if (offset) { + if (offset >= (uint64_t) part_length << 9) { + printf("Device offset is too high\n"); + return 0; + } + part_start += offset >> 9; + part_length -= offset >> 9; + debug("after offset: start %lu, length %lu\n", part_start, part_length); + } + + if (length) { + if (length > (uint64_t) part_length << 9) { + printf("Specified length exceeds the size of device\n"); + return 0; + } + part_length = length >> 9; + debug("after length: length %lu\n", part_length); + using_devsize = 0; + } + + strncpy(dev_name, name, sizeof(dev_name)-1); + + return 1; +} + +/* Read a sector from opened device with simple/stupid buffer cache */ +static void *read_sector(unsigned long sector) +{ + unsigned int hash; + void *buf; + int i; + + /* If reading memory, just return the memory as the buffer */ + if (dev_type == DISK_MEM) { + unsigned long phys = sector << 9; + //debug("mem: %#lx\n", phys); + return phys_to_virt(phys); + } + + /* Search in the cache */ + hash = sector % NUM_CACHE; + buf = buf_cache[hash]; + if (cache_sect[hash] != sector) { + cache_sect[hash] = (unsigned long) -1; + switch (dev_type) { +#ifdef IDE_DISK + case DISK_IDE: + if (ide_read(dev_drive, sector, buf) != 0) + goto readerr; + break; +#endif +#ifdef USB_DISK + case DISK_USB: + if (usb_read(dev_drive, sector, buf) != 0) + goto readerr; + break; +#endif + default: + printf("read_sector: device not open\n"); + return 0; + } + cache_sect[hash] = sector; + } +#if 0 + printf("in read_sector:\n"); + for(i=0;i<128;i++) { + if((i%4)==0) printf("\n %08x:",i*4); + printf(" %08x ",(uint32_t)*((uint32_t *)buf+i)); + } +#endif + + return buf; + +readerr: + printf("Disk read error dev_type=%d drive=%d sector=%x\n", + dev_type, dev_drive, sector); + dev_name[0] = '\0'; /* force re-open the device next time */ + return 0; +} + +int devread(unsigned long sector, unsigned long byte_offset, + unsigned long byte_len, void *buf) +{ + char *sector_buffer; + char *dest = buf; + unsigned long len; + int i; + + sector += byte_offset >> 9; + byte_offset &= 0x1ff; + + if (sector + ((byte_len + 0x1ff) >> 9) > part_length) { + printf("Attempt to read out of device/partition\n"); + debug("sector=%x part_length=%x byte_len=%x\n", + sector, part_length, byte_len); + return 0; + } + + while (byte_len > 0) { + sector_buffer = read_sector(part_start + sector); + if (!sector_buffer) { + debug("read sector failed\n"); + return 0; + } + len = 512 - byte_offset; + if (len > byte_len) + len = byte_len; + memcpy(dest, sector_buffer + byte_offset, len); + sector++; + byte_offset = 0; + byte_len -= len; + dest += len; + } + + return 1; +} diff --git a/src/filo/fs/eltorito.c b/src/filo/fs/eltorito.c new file mode 100644 index 00000000..30b663d0 --- /dev/null +++ b/src/filo/fs/eltorito.c @@ -0,0 +1,147 @@ + +#include +#include +#include + +#define DEBUG_THIS DEBUG_ELTORITO +#include + +#define ELTORITO_PLATFORM_X86 0 +#define ELTORITO_PLATFORM_PPC 1 +#define ELTORITO_PLATFORM_MAC 2 +#include + +#ifndef ELTORITO_PLATFORM +#error "ELTORITO_PLATFORM is not defined for this arch" +#endif + +/* El Torito boot record at sector 0x11 of bootable CD */ +struct boot_record { + uint8_t ind; + uint8_t iso_id[5]; + uint8_t version; + uint8_t boot_id[32]; + uint8_t reserved[32]; + uint8_t catalog_offset[4]; +}; + +/* First entry of the catalog */ +struct validation_entry { + uint8_t header_id; + uint8_t platform; + uint8_t reserved[2]; + uint8_t id[24]; + uint8_t checksum[2]; + uint8_t key55; + uint8_t keyAA; +}; + +/* Initial/Default catalog entry */ +struct default_entry { + uint8_t boot_id; + uint8_t media_type; +#define MEDIA_MASK 0x0f +#define MEDIA_NOEMU 0 +#define MEDIA_1200_FD 1 +#define MEDIA_1440_FD 2 +#define MEDIA_2880_FD 3 +#define MEDIA_HD 4 + uint8_t load_segment[2]; + uint8_t system_type; + uint8_t reserved; + uint8_t sector_count[2]; + uint8_t start_sector[4]; + uint8_t reserved_too[20]; +}; + +/* Find El-Torito boot disk image */ +int open_eltorito_image(int part, unsigned long *offset_p, + unsigned long *length_p) +{ + struct boot_record boot_record; + uint32_t cat_offset; + uint8_t catalog[2048]; + struct validation_entry *ve; + int i, sum; + struct default_entry *de; + + /* We always use 512-byte "soft sector", but + * El-Torito uses 2048-byte CD-ROM sector */ + + /* Boot Record is at sector 0x11 */ + if (!devread(0x11<<2, 0, sizeof boot_record, &boot_record)) + return 0; + + if (boot_record.ind != 0 + || memcmp(boot_record.iso_id, "CD001", 5) != 0 + || memcmp(boot_record.boot_id, "EL TORITO SPECIFICATION", 23) + != 0) { + debug("No El-Torito signature\n"); + return PARTITION_UNKNOWN; + } + + if (part != 0) { + printf("El-Torito entries other than Initial/Default is not supported\n"); + return 0; + } + + cat_offset = get_le32(boot_record.catalog_offset); + debug("El-Torito boot catalog at sector %u\n", cat_offset); + if (!devread(cat_offset<<2, 0, 2048, catalog)) + return 0; + + /* Validate the catalog */ + ve = (void *) catalog; + //debug_hexdump(ve, sizeof *ve); + if (ve->header_id != 1 || ve->key55 != 0x55 || ve->keyAA != 0xAA) { + printf("Invalid El Torito boot catalog\n"); + return 0; + } + /* All words must sum up to zero */ + sum = 0; + for (i = 0; i < sizeof(*ve); i += 2) + sum += get_le16(&catalog[i]); + sum &= 0xffff; + if (sum != 0) { + printf("El Torito boot catalog verify failed\n"); + return 0; + } + debug("id='%.*s'\n", sizeof ve->id, ve->id); + + /* Platform check is warning only, because we won't directly execute + * the image. Just mounting it should be safe. */ + if (ve->platform != ELTORITO_PLATFORM){ + debugx("WARNING: Boot disk for different platform: %d\n", ve->platform); + } + + /* Just support initial/default entry for now */ + de = (void *) (ve + 1); + if (de->boot_id != 0x88) { + debugx("WARNING: Default boot entry is not bootable\n"); + } + + switch (de->media_type & MEDIA_MASK) { + case MEDIA_NOEMU: + printf("Disc doesn't use boot disk emulation\n"); + return 0; + case MEDIA_1200_FD: + *length_p = 1200*1024/512; + break; + case MEDIA_1440_FD: + *length_p = 1440*1024/512; + break; + case MEDIA_2880_FD: + *length_p = 2880*1024/512; + break; + case MEDIA_HD: + /* FIXME: read partition table and return first partition. + * Spec states emulation HD has only one partition and it must + * be the first partition */ + printf("Disc uses hard disk emulation - not supported\n"); + return 0; + } + *offset_p = get_le32(de->start_sector) << 2; + debug("offset=%#lx length=%#lx\n", *offset_p, *length_p); + + return 1; +} diff --git a/src/filo/fs/fat.h b/src/filo/fs/fat.h new file mode 100644 index 00000000..7fed6bac --- /dev/null +++ b/src/filo/fs/fat.h @@ -0,0 +1,100 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2001 Free Software Foundation, Inc. + * + * 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. + */ + + +/* + * Defines for the FAT BIOS Parameter Block (embedded in the first block + * of the partition. + */ + +typedef __signed__ char __s8; +typedef unsigned char __u8; +typedef __signed__ short __s16; +typedef unsigned short __u16; +typedef __signed__ int __s32; +typedef unsigned int __u32; + +/* Note that some shorts are not aligned, and must therefore + * be declared as array of two bytes. + */ +struct fat_bpb { + __s8 ignored[3]; /* Boot strap short or near jump */ + __s8 system_id[8]; /* Name - can be used to special case + partition manager volumes */ + __u8 bytes_per_sect[2]; /* bytes per logical sector */ + __u8 sects_per_clust;/* sectors/cluster */ + __u8 reserved_sects[2]; /* reserved sectors */ + __u8 num_fats; /* number of FATs */ + __u8 dir_entries[2]; /* root directory entries */ + __u8 short_sectors[2]; /* number of sectors */ + __u8 media; /* media code (unused) */ + __u16 fat_length; /* sectors/FAT */ + __u16 secs_track; /* sectors per track */ + __u16 heads; /* number of heads */ + __u32 hidden; /* hidden sectors (unused) */ + __u32 long_sectors; /* number of sectors (if short_sectors == 0) */ + + /* The following fields are only used by FAT32 */ + __u32 fat32_length; /* sectors/FAT */ + __u16 flags; /* bit 8: fat mirroring, low 4: active fat */ + __u8 version[2]; /* major, minor filesystem version */ + __u32 root_cluster; /* first cluster in root directory */ + __u16 info_sector; /* filesystem info sector */ + __u16 backup_boot; /* backup boot sector */ + __u16 reserved2[6]; /* Unused */ +}; + +#define FAT_CVT_U16(bytarr) (* (__u16*)(bytarr)) + +/* + * Defines how to differentiate a 12-bit and 16-bit FAT. + */ + +#define FAT_MAX_12BIT_CLUST 4087 /* 4085 + 2 */ + +/* + * Defines for the file "attribute" byte + */ + +#define FAT_ATTRIB_OK_MASK 0x37 +#define FAT_ATTRIB_NOT_OK_MASK 0xC8 +#define FAT_ATTRIB_DIR 0x10 +#define FAT_ATTRIB_LONGNAME 0x0F + +/* + * Defines for FAT directory entries + */ + +#define FAT_DIRENTRY_LENGTH 32 + +#define FAT_DIRENTRY_ATTRIB(entry) \ + (*((unsigned char *) (entry+11))) +#define FAT_DIRENTRY_VALID(entry) \ + ( ((*((unsigned char *) entry)) != 0) \ + && ((*((unsigned char *) entry)) != 0xE5) \ + && !(FAT_DIRENTRY_ATTRIB(entry) & FAT_ATTRIB_NOT_OK_MASK) ) +#define FAT_DIRENTRY_FIRST_CLUSTER(entry) \ + ((*((unsigned short *) (entry+26)))+(*((unsigned short *) (entry+20)) << 16)) +#define FAT_DIRENTRY_FILELENGTH(entry) \ + (*((unsigned long *) (entry+28))) + +#define FAT_LONGDIR_ID(entry) \ + (*((unsigned char *) (entry))) +#define FAT_LONGDIR_ALIASCHECKSUM(entry) \ + (*((unsigned char *) (entry+13))) diff --git a/src/filo/fs/filesys.h b/src/filo/fs/filesys.h new file mode 100644 index 00000000..f81bebe7 --- /dev/null +++ b/src/filo/fs/filesys.h @@ -0,0 +1,233 @@ +/* GRUB compatibility header */ + +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2003 Free Software Foundation, Inc. + * + * 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. + */ + +//#include +#include + +#include + +/* This disables some portion of code */ +#define STAGE1_5 1 + +static inline int +substring (const char *s1, const char *s2) +{ + while (*s1 == *s2) + { + /* The strings match exactly. */ + if (! *(s1++)) + return 0; + s2 ++; + } + + /* S1 is a substring of S2. */ + if (*s1 == 0) + return -1; + + /* S1 isn't a substring. */ + return 1; +} + +#define grub_memmove memmove +#define grub_strcmp strcmp + +#define MAXINT 0x7fffffff + +/* This is only used by fsys_* to determine if it's hard disk. If it is, + * they try to guess filesystem type by partition type. I guess it is + * not necessory, so hardcoded to 0 (first floppy) --ts1 */ +#define current_drive 0 + +/* Ditto */ +#define current_slice 0 + +extern unsigned long part_start; +extern unsigned long part_length; +extern int filepos; +extern int filemax; +extern int fsmax; + +/* Error codes (descriptions are in common.c) */ +typedef enum +{ + ERR_NONE = 0, + ERR_BAD_FILENAME, + ERR_BAD_FILETYPE, + ERR_BAD_GZIP_DATA, + ERR_BAD_GZIP_HEADER, + ERR_BAD_PART_TABLE, + ERR_BAD_VERSION, + ERR_BELOW_1MB, + ERR_BOOT_COMMAND, + ERR_BOOT_FAILURE, + ERR_BOOT_FEATURES, + ERR_DEV_FORMAT, + ERR_DEV_VALUES, + ERR_EXEC_FORMAT, + ERR_FILELENGTH, + ERR_FILE_NOT_FOUND, + ERR_FSYS_CORRUPT, + ERR_FSYS_MOUNT, + ERR_GEOM, + ERR_NEED_LX_KERNEL, + ERR_NEED_MB_KERNEL, + ERR_NO_DISK, + ERR_NO_PART, + ERR_NUMBER_PARSING, + ERR_OUTSIDE_PART, + ERR_READ, + ERR_SYMLINK_LOOP, + ERR_UNRECOGNIZED, + ERR_WONT_FIT, + ERR_WRITE, + ERR_BAD_ARGUMENT, + ERR_UNALIGNED, + ERR_PRIVILEGED, + ERR_DEV_NEED_INIT, + ERR_NO_DISK_SPACE, + ERR_NUMBER_OVERFLOW, + + MAX_ERR_NUM +} grub_error_t; + +extern grub_error_t errnum; + +#define grub_open file_open +#define grub_read file_read +#define grub_seek file_seek +#define grub_close file_close + +/* instrumentation variables */ +/* (Not used in FILO) */ +extern void (*disk_read_hook) (int, int, int); +extern void (*disk_read_func) (int, int, int); + +#define FSYS_BUFLEN 0x8000 +extern char FSYS_BUF[FSYS_BUFLEN]; + +#define print_possibilities 0 + +#define SECTOR_SIZE 512 +#define SECTOR_BITS 9 + +#ifdef FSYS_FAT +int fat_mount (void); +int fat_read (char *buf, int len); +int fat_dir (char *dirname); +#endif + +#ifdef FSYS_EXT2FS +int ext2fs_mount (void); +int ext2fs_read (char *buf, int len); +int ext2fs_dir (char *dirname); +#endif + +#ifdef FSYS_MINIX +int minix_mount (void); +int minix_read (char *buf, int len); +int minix_dir (char *dirname); +#endif + +#ifdef FSYS_REISERFS +int reiserfs_mount (void); +int reiserfs_read (char *buf, int len); +int reiserfs_dir (char *dirname); +int reiserfs_embed (int *start_sector, int needed_sectors); +#endif + +#ifdef FSYS_JFS +int jfs_mount (void); +int jfs_read (char *buf, int len); +int jfs_dir (char *dirname); +int jfs_embed (int *start_sector, int needed_sectors); +#endif + +#ifdef FSYS_XFS +int xfs_mount (void); +int xfs_read (char *buf, int len); +int xfs_dir (char *dirname); +#endif + +#ifdef FSYS_ISO9660 +int iso9660_mount (void); +int iso9660_read (char *buf, int len); +int iso9660_dir (char *dirname); +#endif + +/* This is not a flag actually, but used as if it were a flag. */ +#define PC_SLICE_TYPE_HIDDEN_FLAG 0x10 + +#define PC_SLICE_TYPE_NONE 0 +#define PC_SLICE_TYPE_FAT12 1 +#define PC_SLICE_TYPE_FAT16_LT32M 4 +#define PC_SLICE_TYPE_EXTENDED 5 +#define PC_SLICE_TYPE_FAT16_GT32M 6 +#define PC_SLICE_TYPE_FAT32 0xb +#define PC_SLICE_TYPE_FAT32_LBA 0xc +#define PC_SLICE_TYPE_FAT16_LBA 0xe +#define PC_SLICE_TYPE_WIN95_EXTENDED 0xf +#define PC_SLICE_TYPE_EZD 0x55 +#define PC_SLICE_TYPE_MINIX 0x80 +#define PC_SLICE_TYPE_LINUX_MINIX 0x81 +#define PC_SLICE_TYPE_EXT2FS 0x83 +#define PC_SLICE_TYPE_LINUX_EXTENDED 0x85 +#define PC_SLICE_TYPE_VSTAFS 0x9e +#define PC_SLICE_TYPE_DELL_UTIL 0xde +#define PC_SLICE_TYPE_LINUX_RAID 0xfd + +/* For convinience. */ +/* Check if TYPE is a FAT partition type. Clear the hidden flag before + the check, to allow the user to mount a hidden partition in GRUB. */ +#define IS_PC_SLICE_TYPE_FAT(type) \ + ({ int _type = (type) & ~PC_SLICE_TYPE_HIDDEN_FLAG; \ + _type == PC_SLICE_TYPE_FAT12 \ + || _type == PC_SLICE_TYPE_FAT16_LT32M \ + || _type == PC_SLICE_TYPE_FAT16_GT32M \ + || _type == PC_SLICE_TYPE_FAT16_LBA \ + || _type == PC_SLICE_TYPE_FAT32 \ + || _type == PC_SLICE_TYPE_FAT32_LBA \ + || _type == PC_SLICE_TYPE_DELL_UTIL; }) + +#define IS_PC_SLICE_TYPE_MINIX(type) \ + (((type) == PC_SLICE_TYPE_MINIX) \ + || ((type) == PC_SLICE_TYPE_LINUX_MINIX)) + +#define IS_PC_SLICE_TYPE_BSD_WITH_FS(type,fs) 0 + +/* possible values for the *BSD-style partition type */ +#define FS_UNUSED 0 /* unused */ +#define FS_SWAP 1 /* swap */ +#define FS_V6 2 /* Sixth Edition */ +#define FS_V7 3 /* Seventh Edition */ +#define FS_SYSV 4 /* System V */ +#define FS_V71K 5 /* V7 with 1K blocks (4.1, 2.9) */ +#define FS_V8 6 /* Eighth Edition, 4K blocks */ +#define FS_BSDFFS 7 /* 4.2BSD fast file system */ +#define FS_MSDOS 8 /* MSDOS file system */ +#define FS_BSDLFS 9 /* 4.4BSD log-structured file system */ +#define FS_OTHER 10 /* in use, but unknown/unsupported */ +#define FS_HPFS 11 /* OS/2 high-performance file system */ +#define FS_ISO9660 12 /* ISO 9660, normally CD-ROM */ +#define FS_BOOT 13 /* partition contains bootstrap */ +#define FS_ADOS 14 /* AmigaDOS fast file system */ +#define FS_HFS 15 /* Macintosh HFS */ +#define FS_FILECORE 16 /* Acorn Filecore Filing System */ +#define FS_EXT2FS 17 /* Linux Extended 2 file system */ diff --git a/src/filo/fs/fsys_ext2fs.c b/src/filo/fs/fsys_ext2fs.c new file mode 100644 index 00000000..62c6e80f --- /dev/null +++ b/src/filo/fs/fsys_ext2fs.c @@ -0,0 +1,779 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999, 2001 Free Software Foundation, Inc. + * + * 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. + */ + +#ifdef FSYS_EXT2FS + +#include "shared.h" +#include "filesys.h" +#include +#include "string.h" + +static int mapblock1, mapblock2; + +/* sizes are always in bytes, BLOCK values are always in DEV_BSIZE (sectors) */ +#define DEV_BSIZE 512 + +/* include/linux/fs.h */ +#define BLOCK_SIZE 1024 /* initial block size for superblock read */ +/* made up, defaults to 1 but can be passed via mount_opts */ +#define WHICH_SUPER 1 +/* kind of from fs/ext2/super.c */ +#define SBLOCK (WHICH_SUPER * BLOCK_SIZE / DEV_BSIZE) /* = 2 */ + +/* include/asm-i386/types.h */ +typedef __signed__ char __s8; +typedef unsigned char __u8; +typedef __signed__ short __s16; +typedef unsigned short __u16; +typedef __signed__ int __s32; +typedef unsigned int __u32; + +/* + * Constants relative to the data blocks, from ext2_fs.h + */ +#define EXT2_NDIR_BLOCKS 12 +#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS +#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1) +#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1) +#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1) + +/* include/linux/ext2_fs.h */ +struct ext2_super_block + { + __u32 s_inodes_count; /* Inodes count */ + __u32 s_blocks_count; /* Blocks count */ + __u32 s_r_blocks_count; /* Reserved blocks count */ + __u32 s_free_blocks_count; /* Free blocks count */ + __u32 s_free_inodes_count; /* Free inodes count */ + __u32 s_first_data_block; /* First Data Block */ + __u32 s_log_block_size; /* Block size */ + __s32 s_log_frag_size; /* Fragment size */ + __u32 s_blocks_per_group; /* # Blocks per group */ + __u32 s_frags_per_group; /* # Fragments per group */ + __u32 s_inodes_per_group; /* # Inodes per group */ + __u32 s_mtime; /* Mount time */ + __u32 s_wtime; /* Write time */ + __u16 s_mnt_count; /* Mount count */ + __s16 s_max_mnt_count; /* Maximal mount count */ + __u16 s_magic; /* Magic signature */ + __u16 s_state; /* File system state */ + __u16 s_errors; /* Behaviour when detecting errors */ + __u16 s_pad; + __u32 s_lastcheck; /* time of last check */ + __u32 s_checkinterval; /* max. time between checks */ + __u32 s_creator_os; /* OS */ + __u32 s_rev_level; /* Revision level */ + __u16 s_def_resuid; /* Default uid for reserved blocks */ + __u16 s_def_resgid; /* Default gid for reserved blocks */ + __u32 s_reserved[235]; /* Padding to the end of the block */ + }; + +struct ext2_group_desc + { + __u32 bg_block_bitmap; /* Blocks bitmap block */ + __u32 bg_inode_bitmap; /* Inodes bitmap block */ + __u32 bg_inode_table; /* Inodes table block */ + __u16 bg_free_blocks_count; /* Free blocks count */ + __u16 bg_free_inodes_count; /* Free inodes count */ + __u16 bg_used_dirs_count; /* Directories count */ + __u16 bg_pad; + __u32 bg_reserved[3]; + }; + +struct ext2_inode + { + __u16 i_mode; /* File mode */ + __u16 i_uid; /* Owner Uid */ + __u32 i_size; /* 4: Size in bytes */ + __u32 i_atime; /* Access time */ + __u32 i_ctime; /* 12: Creation time */ + __u32 i_mtime; /* Modification time */ + __u32 i_dtime; /* 20: Deletion Time */ + __u16 i_gid; /* Group Id */ + __u16 i_links_count; /* 24: Links count */ + __u32 i_blocks; /* Blocks count */ + __u32 i_flags; /* 32: File flags */ + union + { + struct + { + __u32 l_i_reserved1; + } + linux1; + struct + { + __u32 h_i_translator; + } + hurd1; + struct + { + __u32 m_i_reserved1; + } + masix1; + } + osd1; /* OS dependent 1 */ + __u32 i_block[EXT2_N_BLOCKS]; /* 40: Pointers to blocks */ + __u32 i_version; /* File version (for NFS) */ + __u32 i_file_acl; /* File ACL */ + __u32 i_dir_acl; /* Directory ACL */ + __u32 i_faddr; /* Fragment address */ + union + { + struct + { + __u8 l_i_frag; /* Fragment number */ + __u8 l_i_fsize; /* Fragment size */ + __u16 i_pad1; + __u32 l_i_reserved2[2]; + } + linux2; + struct + { + __u8 h_i_frag; /* Fragment number */ + __u8 h_i_fsize; /* Fragment size */ + __u16 h_i_mode_high; + __u16 h_i_uid_high; + __u16 h_i_gid_high; + __u32 h_i_author; + } + hurd2; + struct + { + __u8 m_i_frag; /* Fragment number */ + __u8 m_i_fsize; /* Fragment size */ + __u16 m_pad1; + __u32 m_i_reserved2[2]; + } + masix2; + } + osd2; /* OS dependent 2 */ + }; + +/* linux/limits.h */ +#define NAME_MAX 255 /* # chars in a file name */ + +/* linux/posix_type.h */ +typedef long linux_off_t; + +/* linux/ext2fs.h */ +#define EXT2_NAME_LEN 255 +struct ext2_dir_entry + { + __u32 inode; /* Inode number */ + __u16 rec_len; /* Directory entry length */ + __u8 name_len; /* Name length */ + __u8 file_type; + char name[EXT2_NAME_LEN]; /* File name */ + }; + +/* linux/ext2fs.h */ +/* + * EXT2_DIR_PAD defines the directory entries boundaries + * + * NOTE: It must be a multiple of 4 + */ +#define EXT2_DIR_PAD 4 +#define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1) +#define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \ + ~EXT2_DIR_ROUND) + + +/* ext2/super.c */ +#define log2(n) ffz(~(n)) + +#define EXT2_SUPER_MAGIC 0xEF53 /* include/linux/ext2_fs.h */ +#define EXT2_ROOT_INO 2 /* include/linux/ext2_fs.h */ +#define PATH_MAX 1024 /* include/linux/limits.h */ +#define MAX_LINK_COUNT 5 /* number of symbolic links to follow */ + +/* made up, these are pointers into FSYS_BUF */ +/* read once, always stays there: */ +#define SUPERBLOCK \ + ((struct ext2_super_block *)(FSYS_BUF)) +#define GROUP_DESC \ + ((struct ext2_group_desc *) \ + ((int)SUPERBLOCK + sizeof(struct ext2_super_block))) +#define INODE \ + ((struct ext2_inode *)((int)GROUP_DESC + EXT2_BLOCK_SIZE(SUPERBLOCK))) +#define DATABLOCK1 \ + ((int)((int)INODE + sizeof(struct ext2_inode))) +#define DATABLOCK2 \ + ((int)((int)DATABLOCK1 + EXT2_BLOCK_SIZE(SUPERBLOCK))) + +/* linux/ext2_fs.h */ +#define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (__u32)) +#define EXT2_ADDR_PER_BLOCK_BITS(s) (log2(EXT2_ADDR_PER_BLOCK(s))) + +/* linux/ext2_fs.h */ +#define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10) +/* kind of from ext2/super.c */ +#define EXT2_BLOCK_SIZE(s) (1 << EXT2_BLOCK_SIZE_BITS(s)) +/* linux/ext2fs.h */ +#define EXT2_DESC_PER_BLOCK(s) \ + (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_group_desc)) +/* linux/stat.h */ +#define S_IFMT 00170000 +#define S_IFLNK 0120000 +#define S_IFREG 0100000 +#define S_IFDIR 0040000 +#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) + +/* include/asm-i386/bitops.h */ +/* + * ffz = Find First Zero in word. Undefined if no zero exists, + * so code should check against ~0UL first.. + */ +static __inline__ unsigned long +ffz (unsigned long word) +{ + __asm__ ("bsfl %1,%0" +: "=r" (word) +: "r" (~word)); + return word; +} + +/* check filesystem types and read superblock into memory buffer */ +int +ext2fs_mount (void) +{ + int retval = 1; + + if ((((current_drive & 0x80) || (current_slice != 0)) + && (current_slice != PC_SLICE_TYPE_EXT2FS) + && (current_slice != PC_SLICE_TYPE_LINUX_RAID) + && (! IS_PC_SLICE_TYPE_BSD_WITH_FS (current_slice, FS_EXT2FS)) + && (! IS_PC_SLICE_TYPE_BSD_WITH_FS (current_slice, FS_OTHER))) + || part_length < (SBLOCK + (sizeof (struct ext2_super_block) / DEV_BSIZE)) + || !devread (SBLOCK, 0, sizeof (struct ext2_super_block), + (char *) SUPERBLOCK) + || SUPERBLOCK->s_magic != EXT2_SUPER_MAGIC) + retval = 0; + + return retval; +} + +/* Takes a file system block number and reads it into BUFFER. */ +static int +ext2_rdfsb (int fsblock, int buffer) +{ +#ifdef E2DEBUG + printf ("fsblock %d buffer %d\n", fsblock, buffer); +#endif /* E2DEBUG */ + return devread (fsblock * (EXT2_BLOCK_SIZE (SUPERBLOCK) / DEV_BSIZE), 0, + EXT2_BLOCK_SIZE (SUPERBLOCK), (char *) buffer); +} + +/* from + ext2/inode.c:ext2_bmap() +*/ +/* Maps LOGICAL_BLOCK (the file offset divided by the blocksize) into + a physical block (the location in the file system) via an inode. */ +static int +ext2fs_block_map (int logical_block) +{ + +#ifdef E2DEBUG + unsigned char *i; + for (i = (unsigned char *) INODE; + i < ((unsigned char *) INODE + sizeof (struct ext2_inode)); + i++) + { + printf ("%c", "0123456789abcdef"[*i >> 4]); + printf ("%c", "0123456789abcdef"[*i % 16]); + if (!((i + 1 - (unsigned char *) INODE) % 16)) + { + printf ("\n"); + } + else + { + printf (" "); + } + } + printf ("logical block %d\n", logical_block); +#endif /* E2DEBUG */ + + /* if it is directly pointed to by the inode, return that physical addr */ + if (logical_block < EXT2_NDIR_BLOCKS) + { +#ifdef E2DEBUG + printf ("returning %d\n", (unsigned char *) (INODE->i_block[logical_block])); + printf ("returning %d\n", INODE->i_block[logical_block]); +#endif /* E2DEBUG */ + return INODE->i_block[logical_block]; + } + /* else */ + logical_block -= EXT2_NDIR_BLOCKS; + /* try the indirect block */ + if (logical_block < EXT2_ADDR_PER_BLOCK (SUPERBLOCK)) + { + if (mapblock1 != 1 + && !ext2_rdfsb (INODE->i_block[EXT2_IND_BLOCK], DATABLOCK1)) + { + errnum = ERR_FSYS_CORRUPT; + return -1; + } + mapblock1 = 1; + return ((__u32 *) DATABLOCK1)[logical_block]; + } + /* else */ + logical_block -= EXT2_ADDR_PER_BLOCK (SUPERBLOCK); + /* now try the double indirect block */ + if (logical_block < (1 << (EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK) * 2))) + { + int bnum; + if (mapblock1 != 2 + && !ext2_rdfsb (INODE->i_block[EXT2_DIND_BLOCK], DATABLOCK1)) + { + errnum = ERR_FSYS_CORRUPT; + return -1; + } + mapblock1 = 2; + if ((bnum = (((__u32 *) DATABLOCK1) + [logical_block >> EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK)])) + != mapblock2 + && !ext2_rdfsb (bnum, DATABLOCK2)) + { + errnum = ERR_FSYS_CORRUPT; + return -1; + } + mapblock2 = bnum; + return ((__u32 *) DATABLOCK2) + [logical_block & (EXT2_ADDR_PER_BLOCK (SUPERBLOCK) - 1)]; + } + /* else */ + mapblock2 = -1; + logical_block -= (1 << (EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK) * 2)); + if (mapblock1 != 3 + && !ext2_rdfsb (INODE->i_block[EXT2_TIND_BLOCK], DATABLOCK1)) + { + errnum = ERR_FSYS_CORRUPT; + return -1; + } + mapblock1 = 3; + if (!ext2_rdfsb (((__u32 *) DATABLOCK1) + [logical_block >> (EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK) + * 2)], + DATABLOCK2)) + { + errnum = ERR_FSYS_CORRUPT; + return -1; + } + if (!ext2_rdfsb (((__u32 *) DATABLOCK2) + [(logical_block >> EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK)) + & (EXT2_ADDR_PER_BLOCK (SUPERBLOCK) - 1)], + DATABLOCK2)) + { + errnum = ERR_FSYS_CORRUPT; + return -1; + } + return ((__u32 *) DATABLOCK2) + [logical_block & (EXT2_ADDR_PER_BLOCK (SUPERBLOCK) - 1)]; +} + +/* preconditions: all preconds of ext2fs_block_map */ +int +ext2fs_read (char *buf, int len) +{ + int logical_block; + int offset; + int map; + int ret = 0; + int size = 0; + +#ifdef E2DEBUG + static char hexdigit[] = "0123456789abcdef"; + unsigned char *i; + for (i = (unsigned char *) INODE; + i < ((unsigned char *) INODE + sizeof (struct ext2_inode)); + i++) + { + printf ("%c", hexdigit[*i >> 4]); + printf ("%c", hexdigit[*i % 16]); + if (!((i + 1 - (unsigned char *) INODE) % 16)) + { + printf ("\n"); + } + else + { + printf (" "); + } + } +#endif /* E2DEBUG */ + while (len > 0) + { + /* find the (logical) block component of our location */ + logical_block = filepos >> EXT2_BLOCK_SIZE_BITS (SUPERBLOCK); + offset = filepos & (EXT2_BLOCK_SIZE (SUPERBLOCK) - 1); + map = ext2fs_block_map (logical_block); +#ifdef E2DEBUG + printf ("map=%d\n", map); +#endif /* E2DEBUG */ + if (map < 0) + break; + + size = EXT2_BLOCK_SIZE (SUPERBLOCK); + size -= offset; + if (size > len) + size = len; + + disk_read_func = disk_read_hook; + + devread (map * (EXT2_BLOCK_SIZE (SUPERBLOCK) / DEV_BSIZE), + offset, size, buf); + + disk_read_func = NULL; + + buf += size; + len -= size; + filepos += size; + ret += size; + } + + if (errnum) + ret = 0; + + return ret; +} + + +/* Based on: + def_blk_fops points to + blkdev_open, which calls (I think): + sys_open() + do_open() + open_namei() + dir_namei() which accesses current->fs->root + fs->root was set during original mount: + (something)... which calls (I think): + ext2_read_super() + iget() + __iget() + read_inode() + ext2_read_inode() + uses desc_per_block_bits, which is set in ext2_read_super() + also uses group descriptors loaded during ext2_read_super() + lookup() + ext2_lookup() + ext2_find_entry() + ext2_getblk() + +*/ + +/* preconditions: ext2fs_mount already executed, therefore supblk in buffer + * known as SUPERBLOCK + * returns: 0 if error, nonzero iff we were able to find the file successfully + * postconditions: on a nonzero return, buffer known as INODE contains the + * inode of the file we were trying to look up + * side effects: messes up GROUP_DESC buffer area + */ +int +ext2fs_dir (char *dirname) +{ + int current_ino = EXT2_ROOT_INO; /* start at the root */ + int updir_ino = current_ino; /* the parent of the current directory */ + int group_id; /* which group the inode is in */ + int group_desc; /* fs pointer to that group */ + int desc; /* index within that group */ + int ino_blk; /* fs pointer of the inode's information */ + int str_chk = 0; /* used to hold the results of a string compare */ + struct ext2_group_desc *gdp; + struct ext2_inode *raw_inode; /* inode info corresponding to current_ino */ + + char linkbuf[PATH_MAX]; /* buffer for following symbolic links */ + int link_count = 0; + + char *rest; + char ch; /* temp char holder */ + + int off; /* offset within block of directory entry (off mod blocksize) */ + int loc; /* location within a directory */ + int blk; /* which data blk within dir entry (off div blocksize) */ + long map; /* fs pointer of a particular block from dir entry */ + struct ext2_dir_entry *dp; /* pointer to directory entry */ +#ifdef E2DEBUG + unsigned char *i; +#endif /* E2DEBUG */ + + /* loop invariants: + current_ino = inode to lookup + dirname = pointer to filename component we are cur looking up within + the directory known pointed to by current_ino (if any) + */ + + while (1) + { +#ifdef E2DEBUG + printf ("inode %d\n", current_ino); + printf ("dirname=%s\n", dirname); +#endif /* E2DEBUG */ + + /* look up an inode */ + group_id = (current_ino - 1) / (SUPERBLOCK->s_inodes_per_group); + group_desc = group_id >> log2 (EXT2_DESC_PER_BLOCK (SUPERBLOCK)); + desc = group_id & (EXT2_DESC_PER_BLOCK (SUPERBLOCK) - 1); +#ifdef E2DEBUG + printf ("ipg=%d, dpb=%d\n", SUPERBLOCK->s_inodes_per_group, + EXT2_DESC_PER_BLOCK (SUPERBLOCK)); + printf ("group_id=%d group_desc=%d desc=%d\n", group_id, group_desc, desc); +#endif /* E2DEBUG */ + if (!ext2_rdfsb ( + (WHICH_SUPER + group_desc + SUPERBLOCK->s_first_data_block), + (int) GROUP_DESC)) + { + return 0; + } + gdp = GROUP_DESC; + ino_blk = gdp[desc].bg_inode_table + + (((current_ino - 1) % (SUPERBLOCK->s_inodes_per_group)) + >> log2 (EXT2_BLOCK_SIZE (SUPERBLOCK) / sizeof (struct ext2_inode))); +#ifdef E2DEBUG + printf ("inode table fsblock=%d\n", ino_blk); +#endif /* E2DEBUG */ + if (!ext2_rdfsb (ino_blk, (int) INODE)) + { + return 0; + } + + /* reset indirect blocks! */ + mapblock2 = mapblock1 = -1; + + raw_inode = INODE + + ((current_ino - 1) + & (EXT2_BLOCK_SIZE (SUPERBLOCK) / sizeof (struct ext2_inode) - 1)); +#ifdef E2DEBUG + printf ("ipb=%d, sizeof(inode)=%d\n", + (EXT2_BLOCK_SIZE (SUPERBLOCK) / sizeof (struct ext2_inode)), + sizeof (struct ext2_inode)); + printf ("inode=%x, raw_inode=%x\n", INODE, raw_inode); + printf ("offset into inode table block=%d\n", (int) raw_inode - (int) INODE); + for (i = (unsigned char *) INODE; i <= (unsigned char *) raw_inode; + i++) + { + printf ("%c", "0123456789abcdef"[*i >> 4]); + printf ("%c", "0123456789abcdef"[*i % 16]); + if (!((i + 1 - (unsigned char *) INODE) % 16)) + { + printf ("\n"); + } + else + { + printf (" "); + } + } + printf ("first word=%x\n", *((int *) raw_inode)); +#endif /* E2DEBUG */ + + /* copy inode to fixed location */ + memmove ((void *) INODE, (void *) raw_inode, sizeof (struct ext2_inode)); + +#ifdef E2DEBUG + printf ("first word=%x\n", *((int *) INODE)); +#endif /* E2DEBUG */ + + /* If we've got a symbolic link, then chase it. */ + if (S_ISLNK (INODE->i_mode)) + { + int len; + if (++link_count > MAX_LINK_COUNT) + { + errnum = ERR_SYMLINK_LOOP; + return 0; + } + + /* Find out how long our remaining name is. */ + len = 0; + while (dirname[len] && !isspace (dirname[len])) + len++; + + /* Get the symlink size. */ + filemax = (INODE->i_size); + if (filemax + len > sizeof (linkbuf) - 2) + { + errnum = ERR_FILELENGTH; + return 0; + } + + if (len) + { + /* Copy the remaining name to the end of the symlink data. + Note that DIRNAME and LINKBUF may overlap! */ + memmove (linkbuf + filemax, dirname, len); + } + linkbuf[filemax + len] = '\0'; + + /* Read the symlink data. */ + if (INODE->i_blocks) + { + /* Read the necessary blocks, and reset the file pointer. */ + len = grub_read (linkbuf, filemax); + filepos = 0; + if (!len) + return 0; + } + else + { + /* Copy the data directly from the inode. */ + len = filemax; + memmove (linkbuf, (char *) INODE->i_block, len); + } + +#ifdef E2DEBUG + printf ("symlink=%s\n", linkbuf); +#endif + + dirname = linkbuf; + if (*dirname == '/') + { + /* It's an absolute link, so look it up in root. */ + current_ino = EXT2_ROOT_INO; + updir_ino = current_ino; + } + else + { + /* Relative, so look it up in our parent directory. */ + current_ino = updir_ino; + } + + /* Try again using the new name. */ + continue; + } + + /* if end of filename, INODE points to the file's inode */ + if (!*dirname || isspace (*dirname)) + { + if (!S_ISREG (INODE->i_mode)) + { + errnum = ERR_BAD_FILETYPE; + return 0; + } + + filemax = (INODE->i_size); + return 1; + } + + /* else we have to traverse a directory */ + updir_ino = current_ino; + + /* skip over slashes */ + while (*dirname == '/') + dirname++; + + /* if this isn't a directory of sufficient size to hold our file, abort */ + if (!(INODE->i_size) || !S_ISDIR (INODE->i_mode)) + { + errnum = ERR_BAD_FILETYPE; + return 0; + } + + /* skip to next slash or end of filename (space) */ + for (rest = dirname; (ch = *rest) && !isspace (ch) && ch != '/'; + rest++); + + /* look through this directory and find the next filename component */ + /* invariant: rest points to slash after the next filename component */ + *rest = 0; + loc = 0; + + do + { + +#ifdef E2DEBUG + printf ("dirname=%s, rest=%s, loc=%d\n", dirname, rest, loc); +#endif /* E2DEBUG */ + + /* if our location/byte offset into the directory exceeds the size, + give up */ + if (loc >= INODE->i_size) + { + if (print_possibilities < 0) + { +# if 0 + putchar ('\n'); +# endif + } + else + { + errnum = ERR_FILE_NOT_FOUND; + *rest = ch; + } + return (print_possibilities < 0); + } + + /* else, find the (logical) block component of our location */ + blk = loc >> EXT2_BLOCK_SIZE_BITS (SUPERBLOCK); + + /* we know which logical block of the directory entry we are looking + for, now we have to translate that to the physical (fs) block on + the disk */ + map = ext2fs_block_map (blk); +#ifdef E2DEBUG + printf ("fs block=%d\n", map); +#endif /* E2DEBUG */ + mapblock2 = -1; + if ((map < 0) || !ext2_rdfsb (map, DATABLOCK2)) + { + errnum = ERR_FSYS_CORRUPT; + *rest = ch; + return 0; + } + off = loc & (EXT2_BLOCK_SIZE (SUPERBLOCK) - 1); + dp = (struct ext2_dir_entry *) (DATABLOCK2 + off); + /* advance loc prematurely to next on-disk directory entry */ + loc += dp->rec_len; + + /* NOTE: ext2fs filenames are NOT null-terminated */ + +#ifdef E2DEBUG + printf ("directory entry ino=%d\n", dp->inode); + if (dp->inode) + printf ("entry=%s\n", dp->name); +#endif /* E2DEBUG */ + + if (dp->inode) + { + int saved_c = dp->name[dp->name_len]; + + dp->name[dp->name_len] = 0; + str_chk = substring (dirname, dp->name); + +# ifndef STAGE1_5 + if (print_possibilities && ch != '/' + && (!*dirname || str_chk <= 0)) + { + if (print_possibilities > 0) + print_possibilities = -print_possibilities; + print_a_completion (dp->name); + } +# endif + + dp->name[dp->name_len] = saved_c; + } + + } + while (!dp->inode || (str_chk || (print_possibilities && ch != '/'))); + + current_ino = dp->inode; + *(dirname = rest) = ch; + } + /* never get here */ +} + +#endif /* FSYS_EXT2_FS */ diff --git a/src/filo/fs/fsys_fat.c b/src/filo/fs/fsys_fat.c new file mode 100644 index 00000000..3f85bd6c --- /dev/null +++ b/src/filo/fs/fsys_fat.c @@ -0,0 +1,494 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000, 2001 Free Software Foundation, Inc. + * + * 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. + */ + +#ifdef FSYS_FAT + +#include "shared.h" +#include "filesys.h" +#include "fat.h" +#include +#include "string.h" + +struct fat_superblock +{ + int fat_offset; + int fat_length; + int fat_size; + int root_offset; + int root_max; + int data_offset; + + int num_sectors; + int num_clust; + int clust_eof_marker; + int sects_per_clust; + int sectsize_bits; + int clustsize_bits; + int root_cluster; + + int cached_fat; + int file_cluster; + int current_cluster_num; + int current_cluster; +}; + +/* pointer(s) into filesystem info buffer for DOS stuff */ +#define FAT_SUPER ( (struct fat_superblock *) \ + ( FSYS_BUF + 32256) )/* 512 bytes long */ +#define FAT_BUF ( FSYS_BUF + 30208 ) /* 4 sector FAT buffer */ +#define NAME_BUF ( FSYS_BUF + 29184 ) /* Filename buffer (833 bytes) */ + +#define FAT_CACHE_SIZE 2048 + +static __inline__ unsigned long +log2 (unsigned long word) +{ + __asm__ ("bsfl %1,%0" + : "=r" (word) + : "r" (word)); + return word; +} + +int +fat_mount (void) +{ + struct fat_bpb bpb; + __u32 magic, first_fat; + + /* Check partition type for harddisk */ + if (((current_drive & 0x80) || (current_slice != 0)) + && ! IS_PC_SLICE_TYPE_FAT (current_slice) + && (! IS_PC_SLICE_TYPE_BSD_WITH_FS (current_slice, FS_MSDOS))) + return 0; + + /* Read bpb */ + if (! devread (0, 0, sizeof (bpb), (char *) &bpb)) + return 0; + + /* Check if the number of sectors per cluster is zero here, to avoid + zero division. */ + if (bpb.sects_per_clust == 0) + return 0; + + FAT_SUPER->sectsize_bits = log2 (FAT_CVT_U16 (bpb.bytes_per_sect)); + FAT_SUPER->clustsize_bits + = FAT_SUPER->sectsize_bits + log2 (bpb.sects_per_clust); + + /* Fill in info about super block */ + FAT_SUPER->num_sectors = FAT_CVT_U16 (bpb.short_sectors) + ? FAT_CVT_U16 (bpb.short_sectors) : bpb.long_sectors; + + /* FAT offset and length */ + FAT_SUPER->fat_offset = FAT_CVT_U16 (bpb.reserved_sects); + FAT_SUPER->fat_length = + bpb.fat_length ? bpb.fat_length : bpb.fat32_length; + + /* Rootdir offset and length for FAT12/16 */ + FAT_SUPER->root_offset = + FAT_SUPER->fat_offset + bpb.num_fats * FAT_SUPER->fat_length; + FAT_SUPER->root_max = FAT_DIRENTRY_LENGTH * FAT_CVT_U16(bpb.dir_entries); + + /* Data offset and number of clusters */ + FAT_SUPER->data_offset = + FAT_SUPER->root_offset + + ((FAT_SUPER->root_max - 1) >> FAT_SUPER->sectsize_bits) + 1; + FAT_SUPER->num_clust = + 2 + ((FAT_SUPER->num_sectors - FAT_SUPER->data_offset) + / bpb.sects_per_clust); + FAT_SUPER->sects_per_clust = bpb.sects_per_clust; + + if (!bpb.fat_length) + { + /* This is a FAT32 */ + if (FAT_CVT_U16(bpb.dir_entries)) + return 0; + + if (bpb.flags & 0x0080) + { + /* FAT mirroring is disabled, get active FAT */ + int active_fat = bpb.flags & 0x000f; + if (active_fat >= bpb.num_fats) + return 0; + FAT_SUPER->fat_offset += active_fat * FAT_SUPER->fat_length; + } + + FAT_SUPER->fat_size = 8; + FAT_SUPER->root_cluster = bpb.root_cluster; + + /* Yes the following is correct. FAT32 should be called FAT28 :) */ + FAT_SUPER->clust_eof_marker = 0xffffff8; + } + else + { + if (!FAT_SUPER->root_max) + return 0; + + FAT_SUPER->root_cluster = -1; + if (FAT_SUPER->num_clust > FAT_MAX_12BIT_CLUST) + { + FAT_SUPER->fat_size = 4; + FAT_SUPER->clust_eof_marker = 0xfff8; + } + else + { + FAT_SUPER->fat_size = 3; + FAT_SUPER->clust_eof_marker = 0xff8; + } + } + + + /* Now do some sanity checks */ + + if (FAT_CVT_U16(bpb.bytes_per_sect) != (1 << FAT_SUPER->sectsize_bits) + || FAT_CVT_U16(bpb.bytes_per_sect) != SECTOR_SIZE + || bpb.sects_per_clust != (1 << (FAT_SUPER->clustsize_bits + - FAT_SUPER->sectsize_bits)) + || FAT_SUPER->num_clust <= 2 + || (FAT_SUPER->fat_size * FAT_SUPER->num_clust / (2 * SECTOR_SIZE) + > FAT_SUPER->fat_length)) + return 0; + + /* kbs: Media check on first FAT entry [ported from PUPA] */ + + if (!devread(FAT_SUPER->fat_offset, 0, + sizeof(first_fat), (char *)&first_fat)) + return 0; + + if (FAT_SUPER->fat_size == 8) + { + first_fat &= 0x0fffffff; + magic = 0x0fffff00; + } + else if (FAT_SUPER->fat_size == 4) + { + first_fat &= 0x0000ffff; + magic = 0xff00; + } + else + { + first_fat &= 0x00000fff; + magic = 0x0f00; + } + + if (first_fat != (magic | bpb.media)) + return 0; + + FAT_SUPER->cached_fat = - 2 * FAT_CACHE_SIZE; + return 1; +} + +int +fat_read (char *buf, int len) +{ + int logical_clust; + int offset; + int ret = 0; + int size; + int count = 64; + + if (FAT_SUPER->file_cluster < 0) + { + /* root directory for fat16 */ + size = FAT_SUPER->root_max - filepos; + if (size > len) + size = len; + if (!devread(FAT_SUPER->root_offset, filepos, size, buf)) + return 0; + filepos += size; + return size; + } + + logical_clust = filepos >> FAT_SUPER->clustsize_bits; + offset = (filepos & ((1 << FAT_SUPER->clustsize_bits) - 1)); + if (logical_clust < FAT_SUPER->current_cluster_num) + { + FAT_SUPER->current_cluster_num = 0; + FAT_SUPER->current_cluster = FAT_SUPER->file_cluster; + } + + while (len > 0) + { + int sector; + while (logical_clust > FAT_SUPER->current_cluster_num) + { + /* calculate next cluster */ + int fat_entry = + FAT_SUPER->current_cluster * FAT_SUPER->fat_size; + int next_cluster; + int cached_pos = (fat_entry - FAT_SUPER->cached_fat); + + if (cached_pos < 0 || + (cached_pos + FAT_SUPER->fat_size) > 2*FAT_CACHE_SIZE) + { + FAT_SUPER->cached_fat = (fat_entry & ~(2*SECTOR_SIZE - 1)); + cached_pos = (fat_entry - FAT_SUPER->cached_fat); + sector = FAT_SUPER->fat_offset + + FAT_SUPER->cached_fat / (2*SECTOR_SIZE); + if (!devread (sector, 0, FAT_CACHE_SIZE, (char*) FAT_BUF)) + return 0; + } + next_cluster = * (unsigned long *) (FAT_BUF + (cached_pos >> 1)); + if (FAT_SUPER->fat_size == 3) + { + if (cached_pos & 1) + next_cluster >>= 4; + next_cluster &= 0xFFF; + } + else if (FAT_SUPER->fat_size == 4) + next_cluster &= 0xFFFF; + + if (next_cluster >= FAT_SUPER->clust_eof_marker) + return ret; + if (next_cluster < 2 || next_cluster >= FAT_SUPER->num_clust) + { + errnum = ERR_FSYS_CORRUPT; + return 0; + } + + FAT_SUPER->current_cluster = next_cluster; + FAT_SUPER->current_cluster_num++; + } + + sector = FAT_SUPER->data_offset + + ((FAT_SUPER->current_cluster - 2) << (FAT_SUPER->clustsize_bits + - FAT_SUPER->sectsize_bits)); + size = (1 << FAT_SUPER->clustsize_bits) - offset; + if (size > len) + size = len; + + disk_read_func = disk_read_hook; + + devread(sector, offset, size, buf); + + disk_read_func = NULL; + + len -= size; + buf += size; + ret += size; + filepos += size; + logical_clust++; + offset = 0; + if(count--==0) { + count = 32; + printf("."); + } + } + +// printf("\n"); + return errnum ? 0 : ret; +} + +int +fat_dir (char *dirname) +{ + char *rest, ch, dir_buf[FAT_DIRENTRY_LENGTH]; + char *filename = (char *) NAME_BUF; + int attrib = FAT_ATTRIB_DIR; +#ifndef STAGE1_5 + int do_possibilities = 0; +#endif + + /* XXX I18N: + * the positions 2,4,6 etc are high bytes of a 16 bit unicode char + */ + static unsigned char longdir_pos[] = + { 1, 3, 5, 7, 9, 14, 16, 18, 20, 22, 24, 28, 30 }; + int slot = -2; + int alias_checksum = -1; + + FAT_SUPER->file_cluster = FAT_SUPER->root_cluster; + filepos = 0; + FAT_SUPER->current_cluster_num = MAXINT; + + /* main loop to find desired directory entry */ + loop: + + /* if we have a real file (and we're not just printing possibilities), + then this is where we want to exit */ + + if (!*dirname || isspace (*dirname)) + { + if (attrib & FAT_ATTRIB_DIR) + { + errnum = ERR_BAD_FILETYPE; + return 0; + } + + return 1; + } + + /* continue with the file/directory name interpretation */ + + while (*dirname == '/') + dirname++; + + if (!(attrib & FAT_ATTRIB_DIR)) + { + errnum = ERR_BAD_FILETYPE; + return 0; + } + /* Directories don't have a file size */ + filemax = MAXINT; + + for (rest = dirname; (ch = *rest) && !isspace (ch) && ch != '/'; rest++); + + *rest = 0; + +# ifndef STAGE1_5 + if (print_possibilities && ch != '/') + do_possibilities = 1; +# endif + + while (1) + { + if (fat_read (dir_buf, FAT_DIRENTRY_LENGTH) != FAT_DIRENTRY_LENGTH + || dir_buf[0] == 0) + { + if (!errnum) + { +# ifndef STAGE1_5 + if (print_possibilities < 0) + { +#if 0 + putchar ('\n'); +#endif + return 1; + } +# endif /* STAGE1_5 */ + + errnum = ERR_FILE_NOT_FOUND; + *rest = ch; + } + + return 0; + } + + if (FAT_DIRENTRY_ATTRIB (dir_buf) == FAT_ATTRIB_LONGNAME) + { + /* This is a long filename. The filename is build from back + * to front and may span multiple entries. To bind these + * entries together they all contain the same checksum over + * the short alias. + * + * The id field tells if this is the first entry (the last + * part) of the long filename, and also at which offset this + * belongs. + * + * We just write the part of the long filename this entry + * describes and continue with the next dir entry. + */ + int i, offset; + unsigned char id = FAT_LONGDIR_ID(dir_buf); + + if ((id & 0x40)) + { + id &= 0x3f; + slot = id; + filename[slot * 13] = 0; + alias_checksum = FAT_LONGDIR_ALIASCHECKSUM(dir_buf); + } + + if (id != slot || slot == 0 + || alias_checksum != FAT_LONGDIR_ALIASCHECKSUM(dir_buf)) + { + alias_checksum = -1; + continue; + } + + slot--; + offset = slot * 13; + + for (i=0; i < 13; i++) + filename[offset+i] = dir_buf[longdir_pos[i]]; + continue; + } + + if (!FAT_DIRENTRY_VALID (dir_buf)) + continue; + + if (alias_checksum != -1 && slot == 0) + { + int i; + unsigned char sum; + + slot = -2; + for (sum = 0, i = 0; i< 11; i++) + sum = ((sum >> 1) | (sum << 7)) + dir_buf[i]; + + if (sum == alias_checksum) + { +# ifndef STAGE1_5 + if (do_possibilities) + goto print_filename; +# endif /* STAGE1_5 */ + + if (substring (dirname, filename) == 0) + break; + } + } + + /* XXX convert to 8.3 filename format here */ + { + int i, j, c; + + for (i = 0; i < 8 && (c = filename[i] = tolower (dir_buf[i])) + && !isspace (c); i++); + + filename[i++] = '.'; + + for (j = 0; j < 3 && (c = filename[i + j] = tolower (dir_buf[8 + j])) + && !isspace (c); j++); + + if (j == 0) + i--; + + filename[i + j] = 0; + } + +# ifndef STAGE1_5 + if (do_possibilities) + { + print_filename: + if (substring (dirname, filename) <= 0) + { + if (print_possibilities > 0) + print_possibilities = -print_possibilities; + print_a_completion (filename); + } + continue; + } +# endif /* STAGE1_5 */ + + if (substring (dirname, filename) == 0) + break; + } + + *(dirname = rest) = ch; + + attrib = FAT_DIRENTRY_ATTRIB (dir_buf); + filemax = FAT_DIRENTRY_FILELENGTH (dir_buf); + filepos = 0; + FAT_SUPER->file_cluster = FAT_DIRENTRY_FIRST_CLUSTER (dir_buf); + FAT_SUPER->current_cluster_num = MAXINT; + + /* go back to main loop at top of function */ + goto loop; +} + +#endif /* FSYS_FAT */ diff --git a/src/filo/fs/fsys_iso9660.c b/src/filo/fs/fsys_iso9660.c new file mode 100644 index 00000000..9e667914 --- /dev/null +++ b/src/filo/fs/fsys_iso9660.c @@ -0,0 +1,348 @@ +/* + * ISO 9660 filesystem backend for GRUB (GRand Unified Bootloader) + * including Rock Ridge Extensions support + * + * Copyright (C) 1998, 1999 Kousuke Takai + * + * 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. + */ +/* + * References: + * linux/fs/isofs/rock.[ch] + * mkisofs-1.11.1/diag/isoinfo.c + * mkisofs-1.11.1/iso9660.h + * (all are written by Eric Youngdale) + * + * Modifications by: + * Leonid Lisovskiy 2003 + */ + +/* + * Modified to make it work with FILO + * 2003-10 by SONE Takeshi + */ + +#ifdef FSYS_ISO9660 + +#include +#include "string.h" +#include "shared.h" +#include "filesys.h" +#include "iso9660.h" +#define DEBUG_THIS 1 +#include + +struct iso_superblock { + unsigned long vol_sector; + + unsigned long file_start; +}; + +#define ISO_SUPER ((struct iso_superblock *)(FSYS_BUF)) +#define PRIMDESC ((struct iso_primary_descriptor *)(FSYS_BUF + 2048)) +#define DIRREC ((struct iso_directory_record *)(FSYS_BUF + 4096)) +#define RRCONT_BUF ((unsigned char *)(FSYS_BUF + 6144)) +#define NAME_BUF ((unsigned char *)(FSYS_BUF + 8192)) + + +static inline unsigned long +log2 (unsigned long word) +{ + asm volatile ("bsfl %1,%0" +: "=r" (word) +: "r" (word)); + return word; +} + +static int +iso9660_devread (int sector, int byte_offset, int byte_len, char *buf) +{ + /* FILO uses 512-byte "soft" sector, and ISO-9660 uses 2048-byte + * CD-ROM sector */ + return devread(sector<<2, byte_offset, byte_len, buf); +} + +int +iso9660_mount (void) +{ + unsigned int sector; + + /* + * Because there is no defined slice type ID for ISO-9660 filesystem, + * this test will pass only either (1) if entire disk is used, or + * (2) if current partition is BSD style sub-partition whose ID is + * ISO-9660. + */ + /*if ((current_partition != 0xFFFFFF) + && !IS_PC_SLICE_TYPE_BSD_WITH_FS(current_slice, FS_ISO9660)) + return 0;*/ + + /* + * Currently, only FIRST session of MultiSession disks are supported !!! + */ + for (sector = 16 ; sector < 32 ; sector++) + { + if (!iso9660_devread(sector, 0, sizeof(*PRIMDESC), (char *)PRIMDESC)) + break; + /* check ISO_VD_PRIMARY and ISO_STANDARD_ID */ + if (CHECK4(&PRIMDESC->type, ISO_VD_PRIMARY, 'C', 'D', '0') + && CHECK2(PRIMDESC->id + 3, '0', '1')) + { + ISO_SUPER->vol_sector = sector; + ISO_SUPER->file_start = 0; + fsmax = PRIMDESC->volume_space_size.l; + return 1; + } + } + + return 0; +} + +int +iso9660_dir (char *dirname) +{ + struct iso_directory_record *idr; + RR_ptr_t rr_ptr; + struct rock_ridge *ce_ptr; + unsigned int pathlen; + int size; + unsigned int extent; + unsigned int rr_len; + unsigned char file_type; + unsigned char rr_flag; + + idr = &PRIMDESC->root_directory_record; + ISO_SUPER->file_start = 0; + + do + { + while (*dirname == '/') /* skip leading slashes */ + dirname++; + /* pathlen = strcspn(dirname, "/\n\t "); */ + for (pathlen = 0 ; + dirname[pathlen] + && !isspace(dirname[pathlen]) && dirname[pathlen] != '/' ; + pathlen++) + ; + + size = idr->size.l; + extent = idr->extent.l; + + while (size > 0) + { + if (!iso9660_devread(extent, 0, ISO_SECTOR_SIZE, (char *)DIRREC)) + { + errnum = ERR_FSYS_CORRUPT; + return 0; + } + extent++; + + idr = (struct iso_directory_record *)DIRREC; + for (; idr->length.l > 0; + idr = (struct iso_directory_record *)((char *)idr + idr->length.l) ) + { + const char *name = idr->name; + unsigned int name_len = idr->name_len.l; + + file_type = (idr->flags.l & 2) ? ISO_DIRECTORY : ISO_REGULAR; + if (name_len == 1) + { + if ((name[0] == 0) || /* self */ + (name[0] == 1)) /* parent */ + continue; + } + if (name_len > 2 && CHECK2(name + name_len - 2, ';', '1')) + { + name_len -= 2; /* truncate trailing file version */ + if (name_len > 1 && name[name_len - 1] == '.') + name_len--; /* truncate trailing dot */ + } + + /* + * Parse Rock-Ridge extension + */ + rr_len = (idr->length.l - idr->name_len.l + - (unsigned char)sizeof(struct iso_directory_record) + + (unsigned char)sizeof(idr->name)); + rr_ptr.ptr = ((unsigned char *)idr + idr->name_len.l + + sizeof(struct iso_directory_record) + - sizeof(idr->name)); + if (rr_ptr.i & 1) + rr_ptr.i++, rr_len--; + ce_ptr = NULL; + rr_flag = RR_FLAG_NM | RR_FLAG_PX; + + while (rr_len >= 4) + { + if (rr_ptr.rr->version != 1) + { +#ifndef STAGE1_5 + if (debug) + printf( + "Non-supported version (%d) RockRidge chunk " + "`%c%c'\n", rr_ptr.rr->version, + rr_ptr.rr->signature & 0xFF, + rr_ptr.rr->signature >> 8); +#endif + } + else if (rr_ptr.rr->signature == RRMAGIC('R', 'R') + && rr_ptr.rr->len >= 5) + rr_flag &= rr_ptr.rr->u.rr.flags.l; + else if (rr_ptr.rr->signature == RRMAGIC('N', 'M')) + { + name = rr_ptr.rr->u.nm.name; + name_len = rr_ptr.rr->len - 5; + rr_flag &= ~RR_FLAG_NM; + } + else if (rr_ptr.rr->signature == RRMAGIC('P', 'X') + && rr_ptr.rr->len >= 36) + { + file_type = ((rr_ptr.rr->u.px.mode.l & POSIX_S_IFMT) + == POSIX_S_IFREG + ? ISO_REGULAR + : ((rr_ptr.rr->u.px.mode.l & POSIX_S_IFMT) + == POSIX_S_IFDIR + ? ISO_DIRECTORY : ISO_OTHER)); + rr_flag &= ~RR_FLAG_PX; + } + else if (rr_ptr.rr->signature == RRMAGIC('C', 'E') + && rr_ptr.rr->len >= 28) + ce_ptr = rr_ptr.rr; + if (!rr_flag) + /* + * There is no more extension we expects... + */ + break; + rr_len -= rr_ptr.rr->len; + rr_ptr.ptr += rr_ptr.rr->len; + if (rr_len < 4 && ce_ptr != NULL) + { + /* preserve name before loading new extent. */ + if( RRCONT_BUF <= (unsigned char *)name + && (unsigned char *)name < RRCONT_BUF + ISO_SECTOR_SIZE ) + { + memcpy(NAME_BUF, name, name_len); + name = NAME_BUF; + } + rr_ptr.ptr = RRCONT_BUF + ce_ptr->u.ce.offset.l; + rr_len = ce_ptr->u.ce.size.l; + if (!iso9660_devread(ce_ptr->u.ce.extent.l, 0, ISO_SECTOR_SIZE, RRCONT_BUF)) + { + errnum = 0; /* this is not fatal. */ + break; + } + ce_ptr = NULL; + } + } /* rr_len >= 4 */ + + filemax = MAXINT; + if (name_len >= pathlen + && !__builtin_memcmp(name, dirname, pathlen)) + { + if (dirname[pathlen] == '/' || !print_possibilities) + { + /* + * DIRNAME is directory component of pathname, + * or we are to open a file. + */ + if (pathlen == name_len) + { + if (dirname[pathlen] == '/') + { + if (file_type != ISO_DIRECTORY) + { + errnum = ERR_BAD_FILETYPE; + return 0; + } + goto next_dir_level; + } + if (file_type != ISO_REGULAR) + { + errnum = ERR_BAD_FILETYPE; + return 0; + } + ISO_SUPER->file_start = idr->extent.l; + filepos = 0; + filemax = idr->size.l; + return 1; + } + } + else /* Completion */ + { +#ifndef STAGE1_5 + if (print_possibilities > 0) + print_possibilities = -print_possibilities; + memcpy(NAME_BUF, name, name_len); + NAME_BUF[name_len] = '\0'; + print_a_completion (NAME_BUF); +#endif + } + } + } /* for */ + + size -= ISO_SECTOR_SIZE; + } /* size>0 */ + + if (dirname[pathlen] == '/' || print_possibilities >= 0) + { + errnum = ERR_FILE_NOT_FOUND; + return 0; + } + +next_dir_level: + dirname += pathlen; + + } while (*dirname == '/'); + + return 1; +} + +int +iso9660_read (char *buf, int len) +{ + int sector, blkoffset, size, ret; + + if (ISO_SUPER->file_start == 0) + return 0; + + ret = 0; + blkoffset = filepos & (ISO_SECTOR_SIZE - 1); + sector = filepos >> ISO_SECTOR_BITS; + while (len > 0) + { + size = ISO_SECTOR_SIZE - blkoffset; + if (size > len) + size = len; + + disk_read_func = disk_read_hook; + + if (!iso9660_devread(ISO_SUPER->file_start + sector, blkoffset, size, buf)) + return 0; + + disk_read_func = NULL; + + len -= size; + buf += size; + ret += size; + filepos += size; + sector++; + blkoffset = 0; + } + + return ret; +} + +#endif /* FSYS_ISO9660 */ + diff --git a/src/filo/fs/fsys_jfs.c b/src/filo/fs/fsys_jfs.c new file mode 100644 index 00000000..307f8363 --- /dev/null +++ b/src/filo/fs/fsys_jfs.c @@ -0,0 +1,403 @@ +/* fsys_jfs.c - an implementation for the IBM JFS file system */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2001,2002 Free Software Foundation, Inc. + * + * 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. + */ + +#ifdef FSYS_JFS + +#include "shared.h" +#include "filesys.h" +#include "jfs.h" + +#define MAX_LINK_COUNT 8 + +#define DTTYPE_INLINE 0 +#define DTTYPE_PAGE 1 + +struct jfs_info +{ + int bsize; + int l2bsize; + int bdlog; + int xindex; + int xlastindex; + int sindex; + int slastindex; + int de_index; + int dttype; + xad_t *xad; + ldtentry_t *de; +}; + +static struct jfs_info jfs; + +#define xtpage ((xtpage_t *)FSYS_BUF) +#define dtpage ((dtpage_t *)((char *)FSYS_BUF + 4096)) +#define fileset ((dinode_t *)((char *)FSYS_BUF + 8192)) +#define inode ((dinode_t *)((char *)FSYS_BUF + 8192 + sizeof(dinode_t))) +#define dtroot ((dtroot_t *)(&inode->di_btroot)) + +static ldtentry_t de_always[2] = { + {1, -1, 2, {'.', '.'}}, + {1, -1, 1, {'.'}} +}; + +static int +isinxt (s64 key, s64 offset, s64 len) +{ + return (key >= offset) ? (key < offset + len ? 1 : 0) : 0; +} + +static xad_t * +first_extent (dinode_t *di) +{ + xtpage_t *xtp; + + jfs.xindex = 2; + xtp = (xtpage_t *)&di->di_btroot; + jfs.xad = &xtp->xad[2]; + if (xtp->header.flag & BT_LEAF) { + jfs.xlastindex = xtp->header.nextindex; + } else { + do { + devread (addressXAD (jfs.xad) << jfs.bdlog, 0, + sizeof(xtpage_t), (char *)xtpage); + jfs.xad = &xtpage->xad[2]; + } while (!(xtpage->header.flag & BT_LEAF)); + jfs.xlastindex = xtpage->header.nextindex; + } + + return jfs.xad; +} + +static xad_t * +next_extent (void) +{ + if (++jfs.xindex < jfs.xlastindex) { + } else if (xtpage->header.next) { + devread (xtpage->header.next << jfs.bdlog, 0, + sizeof(xtpage_t), (char *)xtpage); + jfs.xlastindex = xtpage->header.nextindex; + jfs.xindex = XTENTRYSTART; + jfs.xad = &xtpage->xad[XTENTRYSTART]; + } else { + return NULL; + } + return ++jfs.xad; +} + + +static void +di_read (u32 inum, dinode_t *di) +{ + s64 key; + u32 xd, ioffset; + s64 offset; + xad_t *xad; + pxd_t pxd; + + key = (((inum >> L2INOSPERIAG) << L2INOSPERIAG) + 4096) >> jfs.l2bsize; + xd = (inum & (INOSPERIAG - 1)) >> L2INOSPEREXT; + ioffset = ((inum & (INOSPERIAG - 1)) & (INOSPEREXT - 1)) << L2DISIZE; + xad = first_extent (fileset); + do { + offset = offsetXAD (xad); + if (isinxt (key, offset, lengthXAD (xad))) { + devread ((addressXAD (xad) + key - offset) << jfs.bdlog, + 3072 + xd*sizeof(pxd_t), sizeof(pxd_t), (char *)&pxd); + devread (addressPXD (&pxd) << jfs.bdlog, + ioffset, DISIZE, (char *)di); + break; + } + } while ((xad = next_extent ())); +} + +static ldtentry_t * +next_dentry (void) +{ + ldtentry_t *de; + s8 *stbl; + + if (jfs.dttype == DTTYPE_INLINE) { + if (jfs.sindex < jfs.slastindex) { + return (ldtentry_t *)&dtroot->slot[(int)dtroot->header.stbl[jfs.sindex++]]; + } + } else { + de = (ldtentry_t *)dtpage->slot; + stbl = (s8 *)&de[(int)dtpage->header.stblindex]; + if (jfs.sindex < jfs.slastindex) { + return &de[(int)stbl[jfs.sindex++]]; + } else if (dtpage->header.next) { + devread (dtpage->header.next << jfs.bdlog, 0, + sizeof(dtpage_t), (char *)dtpage); + jfs.slastindex = dtpage->header.nextindex; + jfs.sindex = 1; + return &de[(int)((s8 *)&de[(int)dtpage->header.stblindex])[0]]; + } + } + + return (jfs.de_index < 2) ? &de_always[jfs.de_index++] : NULL; +} + +static ldtentry_t * +first_dentry (void) +{ + dtroot_t *dtr; + pxd_t *xd; + idtentry_t *de; + + dtr = (dtroot_t *)&inode->di_btroot; + jfs.sindex = 0; + jfs.de_index = 0; + + de_always[0].inumber = inode->di_parent; + de_always[1].inumber = inode->di_number; + if (dtr->header.flag & BT_LEAF) { + jfs.dttype = DTTYPE_INLINE; + jfs.slastindex = dtr->header.nextindex; + } else { + de = (idtentry_t *)dtpage->slot; + jfs.dttype = DTTYPE_PAGE; + xd = &((idtentry_t *)dtr->slot)[(int)dtr->header.stbl[0]].xd; + for (;;) { + devread (addressPXD (xd) << jfs.bdlog, 0, + sizeof(dtpage_t), (char *)dtpage); + if (dtpage->header.flag & BT_LEAF) + break; + xd = &de[(int)((s8 *)&de[(int)dtpage->header.stblindex])[0]].xd; + } + jfs.slastindex = dtpage->header.nextindex; + } + + return next_dentry (); +} + + +static dtslot_t * +next_dslot (int next) +{ + return (jfs.dttype == DTTYPE_INLINE) + ? (dtslot_t *)&dtroot->slot[next] + : &((dtslot_t *)dtpage->slot)[next]; +} + +static void +uni2ansi (UniChar *uni, char *ansi, int len) +{ + for (; len; len--, uni++) + *ansi++ = (*uni & 0xff80) ? '?' : *(char *)uni; +} + +int +jfs_mount (void) +{ + struct jfs_superblock super; + + if (part_length < MINJFS >> SECTOR_BITS + || !devread (SUPER1_OFF >> SECTOR_BITS, 0, + sizeof(struct jfs_superblock), (char *)&super) + || (super.s_magic != JFS_MAGIC) + || !devread ((AITBL_OFF >> SECTOR_BITS) + FILESYSTEM_I, + 0, DISIZE, (char*)fileset)) { + return 0; + } + + jfs.bsize = super.s_bsize; + jfs.l2bsize = super.s_l2bsize; + jfs.bdlog = jfs.l2bsize - SECTOR_BITS; + + return 1; +} + +int +jfs_read (char *buf, int len) +{ + xad_t *xad; + s64 endofprev, endofcur; + s64 offset, xadlen; + int toread, startpos, endpos; + + startpos = filepos; + endpos = filepos + len; + endofprev = (1ULL << 62) - 1; + xad = first_extent (inode); + do { + offset = offsetXAD (xad); + xadlen = lengthXAD (xad); + if (isinxt (filepos >> jfs.l2bsize, offset, xadlen)) { + endofcur = (offset + xadlen) << jfs.l2bsize; + toread = (endofcur >= endpos) + ? len : (endofcur - filepos); + + disk_read_func = disk_read_hook; + devread (addressXAD (xad) << jfs.bdlog, + filepos - (offset << jfs.l2bsize), toread, buf); + disk_read_func = NULL; + + buf += toread; + len -= toread; + filepos += toread; + } else if (offset > endofprev) { + toread = ((offset << jfs.l2bsize) >= endpos) + ? len : ((offset - endofprev) << jfs.l2bsize); + len -= toread; + filepos += toread; + for (; toread; toread--) { + *buf++ = 0; + } + continue; + } + endofprev = offset + xadlen; + xad = next_extent (); + } while (len > 0 && xad); + + return filepos - startpos; +} + +int +jfs_dir (char *dirname) +{ + char *ptr, *rest, ch; + ldtentry_t *de; + dtslot_t *ds; + u32 inum, parent_inum; + s64 di_size; + u32 di_mode; + int namlen, cmp, n, link_count; + char namebuf[JFS_NAME_MAX + 1], linkbuf[JFS_PATH_MAX]; + + parent_inum = inum = ROOT_I; + link_count = 0; + for (;;) { + di_read (inum, inode); + di_size = inode->di_size; + di_mode = inode->di_mode; + + if ((di_mode & IFMT) == IFLNK) { + if (++link_count > MAX_LINK_COUNT) { + errnum = ERR_SYMLINK_LOOP; + return 0; + } + if (di_size < (di_mode & INLINEEA ? 256 : 128)) { + grub_memmove (linkbuf, inode->di_fastsymlink, di_size); + n = di_size; + } else if (di_size < JFS_PATH_MAX - 1) { + filepos = 0; + filemax = di_size; + n = jfs_read (linkbuf, filemax); + } else { + errnum = ERR_FILELENGTH; + return 0; + } + + inum = (linkbuf[0] == '/') ? ROOT_I : parent_inum; + while (n < (JFS_PATH_MAX - 1) && (linkbuf[n++] = *dirname++)); + linkbuf[n] = 0; + dirname = linkbuf; + continue; + } + + if (!*dirname || isspace (*dirname)) { + if ((di_mode & IFMT) != IFREG) { + errnum = ERR_BAD_FILETYPE; + return 0; + } + filepos = 0; + filemax = di_size; + return 1; + } + + if ((di_mode & IFMT) != IFDIR) { + errnum = ERR_BAD_FILETYPE; + return 0; + } + + for (; *dirname == '/'; dirname++); + + for (rest = dirname; (ch = *rest) && !isspace (ch) && ch != '/'; rest++); + *rest = 0; + + de = first_dentry (); + for (;;) { + namlen = de->namlen; + if (de->next == -1) { + uni2ansi (de->name, namebuf, namlen); + namebuf[namlen] = 0; + } else { + uni2ansi (de->name, namebuf, DTLHDRDATALEN); + ptr = namebuf; + ptr += DTLHDRDATALEN; + namlen -= DTLHDRDATALEN; + ds = next_dslot (de->next); + while (ds->next != -1) { + uni2ansi (ds->name, ptr, DTSLOTDATALEN); + ptr += DTSLOTDATALEN; + namlen -= DTSLOTDATALEN; + ds = next_dslot (ds->next); + } + uni2ansi (ds->name, ptr, namlen); + ptr += namlen; + *ptr = 0; + } + + cmp = (!*dirname) ? -1 : substring (dirname, namebuf); +#ifndef STAGE1_5 + if (print_possibilities && ch != '/' + && cmp <= 0) { + if (print_possibilities > 0) + print_possibilities = -print_possibilities; + print_a_completion (namebuf); + } else +#endif + if (cmp == 0) { + parent_inum = inum; + inum = de->inumber; + *(dirname = rest) = ch; + break; + } + de = next_dentry (); + if (de == NULL) { + if (print_possibilities < 0) + return 1; + + errnum = ERR_FILE_NOT_FOUND; + *rest = ch; + return 0; + } + } + } +} + +int +jfs_embed (int *start_sector, int needed_sectors) +{ + struct jfs_superblock super; + + if (needed_sectors > 63 + || !devread (SUPER1_OFF >> SECTOR_BITS, 0, + sizeof (struct jfs_superblock), + (char *)&super) + || (super.s_magic != JFS_MAGIC)) { + return 0; + } + + *start_sector = 1; + return 1; +} + +#endif /* FSYS_JFS */ diff --git a/src/filo/fs/fsys_minix.c b/src/filo/fs/fsys_minix.c new file mode 100644 index 00000000..5c76796a --- /dev/null +++ b/src/filo/fs/fsys_minix.c @@ -0,0 +1,534 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002 Free Software Foundation, Inc. + * + * 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. + */ + +/* Restrictions: + This is MINIX V1 only (yet) + Disk creation is like: + mkfs.minix -c DEVICE +*/ + +#ifdef FSYS_MINIX + +#include "shared.h" +#include "filesys.h" + +/* #define DEBUG_MINIX */ + +/* indirect blocks */ +static int mapblock1, mapblock2, namelen; + +/* sizes are always in bytes, BLOCK values are always in DEV_BSIZE (sectors) */ +#define DEV_BSIZE 512 + +/* include/linux/fs.h */ +#define BLOCK_SIZE_BITS 10 +#define BLOCK_SIZE (1<. Oh well. */ +#define MINIX_LINK_MAX 250 +#define MINIX2_LINK_MAX 65530 + +#define MINIX_I_MAP_SLOTS 8 +#define MINIX_Z_MAP_SLOTS 64 +#define MINIX_SUPER_MAGIC 0x137F /* original minix fs */ +#define MINIX_SUPER_MAGIC2 0x138F /* minix fs, 30 char names */ +#define MINIX2_SUPER_MAGIC 0x2468 /* minix V2 fs */ +#define MINIX2_SUPER_MAGIC2 0x2478 /* minix V2 fs, 30 char names */ +#define MINIX_VALID_FS 0x0001 /* Clean fs. */ +#define MINIX_ERROR_FS 0x0002 /* fs has errors. */ + +#define MINIX_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix_inode))) +#define MINIX2_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix2_inode))) + +#define MINIX_V1 0x0001 /* original minix fs */ +#define MINIX_V2 0x0002 /* minix V2 fs */ + +/* originally this is : +#define INODE_VERSION(inode) inode->i_sb->u.minix_sb.s_version + here we have */ +#define INODE_VERSION(inode) (SUPERBLOCK->s_version) + +/* + * This is the original minix inode layout on disk. + * Note the 8-bit gid and atime and ctime. + */ +struct minix_inode { + __u16 i_mode; + __u16 i_uid; + __u32 i_size; + __u32 i_time; + __u8 i_gid; + __u8 i_nlinks; + __u16 i_zone[9]; +}; + +/* + * The new minix inode has all the time entries, as well as + * long block numbers and a third indirect block (7+1+1+1 + * instead of 7+1+1). Also, some previously 8-bit values are + * now 16-bit. The inode is now 64 bytes instead of 32. + */ +struct minix2_inode { + __u16 i_mode; + __u16 i_nlinks; + __u16 i_uid; + __u16 i_gid; + __u32 i_size; + __u32 i_atime; + __u32 i_mtime; + __u32 i_ctime; + __u32 i_zone[10]; +}; + +/* + * minix super-block data on disk + */ +struct minix_super_block { + __u16 s_ninodes; + __u16 s_nzones; + __u16 s_imap_blocks; + __u16 s_zmap_blocks; + __u16 s_firstdatazone; + __u16 s_log_zone_size; + __u32 s_max_size; + __u16 s_magic; + __u16 s_state; + __u32 s_zones; +}; + +struct minix_dir_entry { + __u16 inode; + char name[0]; +}; + +/* made up, these are pointers into FSYS_BUF */ +/* read once, always stays there: */ +#define SUPERBLOCK \ + ((struct minix_super_block *)(FSYS_BUF)) +#define INODE \ + ((struct minix_inode *)((int) SUPERBLOCK + BLOCK_SIZE)) +#define DATABLOCK1 \ + ((int)((int)INODE + sizeof(struct minix_inode))) +#define DATABLOCK2 \ + ((int)((int)DATABLOCK1 + BLOCK_SIZE)) + +/* linux/stat.h */ +#define S_IFMT 00170000 +#define S_IFLNK 0120000 +#define S_IFREG 0100000 +#define S_IFDIR 0040000 +#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) + +#define PATH_MAX 1024 /* include/linux/limits.h */ +#define MAX_LINK_COUNT 5 /* number of symbolic links to follow */ + +/* check filesystem types and read superblock into memory buffer */ +int +minix_mount (void) +{ + if (((current_drive & 0x80) || current_slice != 0) + && ! IS_PC_SLICE_TYPE_MINIX (current_slice) + && ! IS_PC_SLICE_TYPE_BSD_WITH_FS (current_slice, FS_OTHER)) + return 0; /* The partition is not of MINIX type */ + + if (part_length < (SBLOCK + + (sizeof (struct minix_super_block) / DEV_BSIZE))) + return 0; /* The partition is too short */ + + if (!devread (SBLOCK, 0, sizeof (struct minix_super_block), + (char *) SUPERBLOCK)) + return 0; /* Cannot read superblock */ + + switch (SUPERBLOCK->s_magic) + { + case MINIX_SUPER_MAGIC: + namelen = 14; + break; + case MINIX_SUPER_MAGIC2: + namelen = 30; + break; + default: + return 0; /* Unsupported type */ + } + + return 1; +} + +/* Takes a file system block number and reads it into BUFFER. */ +static int +minix_rdfsb (int fsblock, int buffer) +{ + return devread (fsblock * (BLOCK_SIZE / DEV_BSIZE), 0, + BLOCK_SIZE, (char *) buffer); +} + +/* Maps LOGICAL_BLOCK (the file offset divided by the blocksize) into + a physical block (the location in the file system) via an inode. */ +static int +minix_block_map (int logical_block) +{ + int i; + + if (logical_block < 7) + return INODE->i_zone[logical_block]; + + logical_block -= 7; + if (logical_block < 512) + { + i = INODE->i_zone[7]; + + if (!i || ((mapblock1 != 1) + && !minix_rdfsb (i, DATABLOCK1))) + { + errnum = ERR_FSYS_CORRUPT; + return -1; + } + mapblock1 = 1; + return ((__u16 *) DATABLOCK1) [logical_block]; + } + + logical_block -= 512; + i = INODE->i_zone[8]; + if (!i || ((mapblock1 != 2) + && !minix_rdfsb (i, DATABLOCK1))) + { + errnum = ERR_FSYS_CORRUPT; + return -1; + } + mapblock1 = 2; + i = ((__u16 *) DATABLOCK1)[logical_block >> 9]; + if (!i || ((mapblock2 != i) + && !minix_rdfsb (i, DATABLOCK2))) + { + errnum = ERR_FSYS_CORRUPT; + return -1; + } + mapblock2 = i; + return ((__u16 *) DATABLOCK2)[logical_block & 511]; +} + +/* read from INODE into BUF */ +int +minix_read (char *buf, int len) +{ + int logical_block; + int offset; + int map; + int ret = 0; + int size = 0; + + while (len > 0) + { + /* find the (logical) block component of our location */ + logical_block = filepos >> BLOCK_SIZE_BITS; + offset = filepos & (BLOCK_SIZE - 1); + map = minix_block_map (logical_block); +#ifdef DEBUG_MINIX + printf ("map=%d\n", map); +#endif + if (map < 0) + break; + + size = BLOCK_SIZE; + size -= offset; + if (size > len) + size = len; + + disk_read_func = disk_read_hook; + + devread (map * (BLOCK_SIZE / DEV_BSIZE), + offset, size, buf); + + disk_read_func = NULL; + + buf += size; + len -= size; + filepos += size; + ret += size; + } + + if (errnum) + ret = 0; + + return ret; +} + +/* preconditions: minix_mount already executed, therefore supblk in buffer + known as SUPERBLOCK + returns: 0 if error, nonzero iff we were able to find the file successfully + postconditions: on a nonzero return, buffer known as INODE contains the + inode of the file we were trying to look up + side effects: none yet */ +int +minix_dir (char *dirname) +{ + int current_ino = MINIX_ROOT_INO; /* start at the root */ + int updir_ino = current_ino; /* the parent of the current directory */ + int ino_blk; /* fs pointer of the inode's info */ + + int str_chk = 0; /* used ot hold the results of a string + compare */ + + struct minix_inode * raw_inode; /* inode info for current_ino */ + + char linkbuf[PATH_MAX]; /* buffer for following sym-links */ + int link_count = 0; + + char * rest; + char ch; + + int off; /* offset within block of directory + entry */ + int loc; /* location within a directory */ + int blk; /* which data blk within dir entry */ + long map; /* fs pointer of a particular block from + dir entry */ + struct minix_dir_entry * dp; /* pointer to directory entry */ + + /* loop invariants: + current_ino = inode to lookup + dirname = pointer to filename component we are cur looking up within + the directory known pointed to by current_ino (if any) */ + +#ifdef DEBUG_MINIX + printf ("\n"); +#endif + + while (1) + { +#ifdef DEBUG_MINIX + printf ("inode %d, dirname %s\n", current_ino, dirname); +#endif + + ino_blk = (2 + SUPERBLOCK->s_imap_blocks + SUPERBLOCK->s_zmap_blocks + + (current_ino - 1) / MINIX_INODES_PER_BLOCK); + if (! minix_rdfsb (ino_blk, (int) INODE)) + return 0; + + /* reset indirect blocks! */ + mapblock2 = mapblock1 = -1; + + raw_inode = INODE + ((current_ino - 1) % MINIX_INODES_PER_BLOCK); + + /* copy inode to fixed location */ + memmove ((void *) INODE, (void *) raw_inode, + sizeof (struct minix_inode)); + + /* If we've got a symbolic link, then chase it. */ + if (S_ISLNK (INODE->i_mode)) + { + int len; + + if (++link_count > MAX_LINK_COUNT) + { + errnum = ERR_SYMLINK_LOOP; + return 0; + } +#ifdef DEBUG_MINIX + printf ("S_ISLNK (%s)\n", dirname); +#endif + + /* Find out how long our remaining name is. */ + len = 0; + while (dirname[len] && !isspace (dirname[len])) + len++; + + /* Get the symlink size. */ + filemax = (INODE->i_size); + if (filemax + len > sizeof (linkbuf) - 2) + { + errnum = ERR_FILELENGTH; + return 0; + } + + if (len) + { + /* Copy the remaining name to the end of the symlink data. + Note that DIRNAME and LINKBUF may overlap! */ + memmove (linkbuf + filemax, dirname, len); + } + linkbuf[filemax + len] = '\0'; + + /* Read the necessary blocks, and reset the file pointer. */ + len = grub_read (linkbuf, filemax); + filepos = 0; + if (!len) + return 0; + +#ifdef DEBUG_MINIX + printf ("symlink=%s\n", linkbuf); +#endif + + dirname = linkbuf; + if (*dirname == '/') + { + /* It's an absolute link, so look it up in root. */ + current_ino = MINIX_ROOT_INO; + updir_ino = current_ino; + } + else + { + /* Relative, so look it up in our parent directory. */ + current_ino = updir_ino; + } + + /* Try again using the new name. */ + continue; + } + + /* If end of filename, INODE points to the file's inode */ + if (!*dirname || isspace (*dirname)) + { + if (!S_ISREG (INODE->i_mode)) + { + errnum = ERR_BAD_FILETYPE; + return 0; + } + + filemax = (INODE->i_size); + return 1; + } + + /* else we have to traverse a directory */ + updir_ino = current_ino; + + /* skip over slashes */ + while (*dirname == '/') + dirname++; + + /* if this isn't a directory of sufficient size to hold our file, + abort */ + if (!(INODE->i_size) || !S_ISDIR (INODE->i_mode)) + { + errnum = ERR_BAD_FILETYPE; + return 0; + } + + /* skip to next slash or end of filename (space) */ + for (rest = dirname; (ch = *rest) && !isspace (ch) && ch != '/'; + rest++); + + /* look through this directory and find the next filename component */ + /* invariant: rest points to slash after the next filename component */ + *rest = 0; + loc = 0; + + do + { +#ifdef DEBUG_MINIX + printf ("dirname=`%s', rest=`%s', loc=%d\n", dirname, rest, loc); +#endif + + /* if our location/byte offset into the directory exceeds the size, + give up */ + if (loc >= INODE->i_size) + { + if (print_possibilities < 0) + { +#if 0 + putchar ('\n'); +#endif + } + else + { + errnum = ERR_FILE_NOT_FOUND; + *rest = ch; + } + return (print_possibilities < 0); + } + + /* else, find the (logical) block component of our location */ + blk = loc >> BLOCK_SIZE_BITS; + + /* we know which logical block of the directory entry we are looking + for, now we have to translate that to the physical (fs) block on + the disk */ + map = minix_block_map (blk); +#ifdef DEBUG_MINIX + printf ("fs block=%d\n", map); +#endif + mapblock2 = -1; + if ((map < 0) || !minix_rdfsb (map, DATABLOCK2)) + { + errnum = ERR_FSYS_CORRUPT; + *rest = ch; + return 0; + } + off = loc & (BLOCK_SIZE - 1); + dp = (struct minix_dir_entry *) (DATABLOCK2 + off); + /* advance loc prematurely to next on-disk directory entry */ + loc += sizeof (dp->inode) + namelen; + + /* NOTE: minix filenames are NULL terminated if < NAMELEN + else exact */ + +#ifdef DEBUG_MINIX + printf ("directory entry ino=%d\n", dp->inode); + if (dp->inode) + printf ("entry=%s\n", dp->name); +#endif + + if (dp->inode) + { + int saved_c = dp->name[namelen]; + + dp->name[namelen] = 0; + str_chk = substring (dirname, dp->name); + +# ifndef STAGE1_5 + if (print_possibilities && ch != '/' + && (!*dirname || str_chk <= 0)) + { + if (print_possibilities > 0) + print_possibilities = -print_possibilities; + print_a_completion (dp->name); + } +# endif + + dp->name[namelen] = saved_c; + } + + } + while (!dp->inode || (str_chk || (print_possibilities && ch != '/'))); + + current_ino = dp->inode; + *(dirname = rest) = ch; + } + /* never get here */ +} + +#endif /* FSYS_MINIX */ diff --git a/src/filo/fs/fsys_reiserfs.c b/src/filo/fs/fsys_reiserfs.c new file mode 100644 index 00000000..fbc2d74a --- /dev/null +++ b/src/filo/fs/fsys_reiserfs.c @@ -0,0 +1,1239 @@ +/* fsys_reiserfs.c - an implementation for the ReiserFS filesystem */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000, 2001 Free Software Foundation, Inc. + * + * 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. + */ + +#ifdef FSYS_REISERFS +#include "shared.h" +#include "filesys.h" +#include +#include "string.h" + +#undef REISERDEBUG + +/* Some parts of this code (mainly the structures and defines) are + * from the original reiser fs code, as found in the linux kernel. + */ + +/* include/asm-i386/types.h */ +typedef __signed__ char __s8; +typedef unsigned char __u8; +typedef __signed__ short __s16; +typedef unsigned short __u16; +typedef __signed__ int __s32; +typedef unsigned int __u32; +typedef unsigned long long __u64; + +/* linux/posix_type.h */ +typedef long linux_off_t; + +/* linux/little_endian.h */ +#define __cpu_to_le64(x) ((__u64) (x)) +#define __le64_to_cpu(x) ((__u64) (x)) +#define __cpu_to_le32(x) ((__u32) (x)) +#define __le32_to_cpu(x) ((__u32) (x)) +#define __cpu_to_le16(x) ((__u16) (x)) +#define __le16_to_cpu(x) ((__u16) (x)) + +/* include/linux/reiser_fs.h */ +/* This is the new super block of a journaling reiserfs system */ +struct reiserfs_super_block +{ + __u32 s_block_count; /* blocks count */ + __u32 s_free_blocks; /* free blocks count */ + __u32 s_root_block; /* root block number */ + __u32 s_journal_block; /* journal block number */ + __u32 s_journal_dev; /* journal device number */ + __u32 s_journal_size; /* size of the journal on FS creation. used to make sure they don't overflow it */ + __u32 s_journal_trans_max; /* max number of blocks in a transaction. */ + __u32 s_journal_magic; /* random value made on fs creation */ + __u32 s_journal_max_batch; /* max number of blocks to batch into a trans */ + __u32 s_journal_max_commit_age; /* in seconds, how old can an async commit be */ + __u32 s_journal_max_trans_age; /* in seconds, how old can a transaction be */ + __u16 s_blocksize; /* block size */ + __u16 s_oid_maxsize; /* max size of object id array */ + __u16 s_oid_cursize; /* current size of object id array */ + __u16 s_state; /* valid or error */ + char s_magic[16]; /* reiserfs magic string indicates that file system is reiserfs */ + __u16 s_tree_height; /* height of disk tree */ + __u16 s_bmap_nr; /* amount of bitmap blocks needed to address each block of file system */ + __u16 s_version; + char s_unused[128]; /* zero filled by mkreiserfs */ +}; + +#define REISERFS_MAX_SUPPORTED_VERSION 2 +#define REISERFS_SUPER_MAGIC_STRING "ReIsErFs" +#define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs" + +#define MAX_HEIGHT 7 + +/* must be correct to keep the desc and commit structs at 4k */ +#define JOURNAL_TRANS_HALF 1018 + +/* first block written in a commit. */ +struct reiserfs_journal_desc { + __u32 j_trans_id; /* id of commit */ + __u32 j_len; /* length of commit. len +1 is the commit block */ + __u32 j_mount_id; /* mount id of this trans*/ + __u32 j_realblock[JOURNAL_TRANS_HALF]; /* real locations for the first blocks */ + char j_magic[12]; +}; + +/* last block written in a commit */ +struct reiserfs_journal_commit { + __u32 j_trans_id; /* must match j_trans_id from the desc block */ + __u32 j_len; /* ditto */ + __u32 j_realblock[JOURNAL_TRANS_HALF]; /* real locations for the last blocks */ + char j_digest[16]; /* md5 sum of all the blocks involved, including desc and commit. not used, kill it */ +}; + +/* this header block gets written whenever a transaction is considered + fully flushed, and is more recent than the last fully flushed + transaction. + fully flushed means all the log blocks and all the real blocks are + on disk, and this transaction does not need to be replayed. +*/ +struct reiserfs_journal_header { + /* id of last fully flushed transaction */ + __u32 j_last_flush_trans_id; + /* offset in the log of where to start replay after a crash */ + __u32 j_first_unflushed_offset; + /* mount id to detect very old transactions */ + __u32 j_mount_id; +}; + +/* magic string to find desc blocks in the journal */ +#define JOURNAL_DESC_MAGIC "ReIsErLB" + + +/* + * directories use this key as well as old files + */ +struct offset_v1 +{ + /* + * for regular files this is the offset to the first byte of the + * body, contained in the object-item, as measured from the start of + * the entire body of the object. + * + * for directory entries, k_offset consists of hash derived from + * hashing the name and using few bits (23 or more) of the resulting + * hash, and generation number that allows distinguishing names with + * hash collisions. If number of collisions overflows generation + * number, we return EEXIST. High order bit is 0 always + */ + __u32 k_offset; + __u32 k_uniqueness; +}; + +struct offset_v2 +{ + /* + * for regular files this is the offset to the first byte of the + * body, contained in the object-item, as measured from the start of + * the entire body of the object. + * + * for directory entries, k_offset consists of hash derived from + * hashing the name and using few bits (23 or more) of the resulting + * hash, and generation number that allows distinguishing names with + * hash collisions. If number of collisions overflows generation + * number, we return EEXIST. High order bit is 0 always + */ + __u64 k_offset:60; + __u64 k_type: 4; +}; + + +struct key +{ + /* packing locality: by default parent directory object id */ + __u32 k_dir_id; + /* object identifier */ + __u32 k_objectid; + /* the offset and node type (old and new form) */ + union + { + struct offset_v1 v1; + struct offset_v2 v2; + } + u; +}; + +#define KEY_SIZE (sizeof (struct key)) + +/* Header of a disk block. More precisely, header of a formatted leaf + or internal node, and not the header of an unformatted node. */ +struct block_head +{ + __u16 blk_level; /* Level of a block in the tree. */ + __u16 blk_nr_item; /* Number of keys/items in a block. */ + __u16 blk_free_space; /* Block free space in bytes. */ + struct key blk_right_delim_key; /* Right delimiting key for this block (supported for leaf level nodes + only) */ +}; +#define BLKH_SIZE (sizeof (struct block_head)) +#define DISK_LEAF_NODE_LEVEL 1 /* Leaf node level. */ + +struct item_head +{ + struct key ih_key; /* Everything in the tree is found by searching for it based on its key.*/ + + union + { + __u16 ih_free_space; /* The free space in the last unformatted node of an indirect item if this + is an indirect item. This equals 0xFFFF iff this is a direct item or + stat data item. Note that the key, not this field, is used to determine + the item type, and thus which field this union contains. */ + __u16 ih_entry_count; /* Iff this is a directory item, this field equals the number of directory + entries in the directory item. */ + } + u; + __u16 ih_item_len; /* total size of the item body */ + __u16 ih_item_location; /* an offset to the item body within the block */ + __u16 ih_version; /* ITEM_VERSION_1 for all old items, + ITEM_VERSION_2 for new ones. + Highest bit is set by fsck + temporary, cleaned after all done */ +}; +/* size of item header */ +#define IH_SIZE (sizeof (struct item_head)) + +#define ITEM_VERSION_1 0 +#define ITEM_VERSION_2 1 +#define IH_KEY_OFFSET(ih) ((ih)->ih_version == ITEM_VERSION_1 \ + ? (ih)->ih_key.u.v1.k_offset \ + : (ih)->ih_key.u.v2.k_offset) + +#define IH_KEY_ISTYPE(ih, type) ((ih)->ih_version == ITEM_VERSION_1 \ + ? (ih)->ih_key.u.v1.k_uniqueness == V1_##type \ + : (ih)->ih_key.u.v2.k_type == V2_##type) + +struct disk_child +{ + unsigned long dc_block_number; /* Disk child's block number. */ + unsigned short dc_size; /* Disk child's used space. */ +}; + +#define DC_SIZE (sizeof (struct disk_child)) + +/* Stat Data on disk. + * + * Note that reiserfs has two different forms of stat data. Luckily + * the fields needed by grub are at the same position. + */ +struct stat_data +{ + __u16 sd_mode; /* file type, permissions */ + __u16 sd_notused1[3]; /* fields not needed by reiserfs */ + __u32 sd_size; /* file size */ + __u32 sd_size_hi; /* file size high 32 bits (since version 2) */ +}; + +struct reiserfs_de_head +{ + __u32 deh_offset; /* third component of the directory entry key */ + __u32 deh_dir_id; /* objectid of the parent directory of the + object, that is referenced by directory entry */ + __u32 deh_objectid;/* objectid of the object, that is referenced by + directory entry */ + __u16 deh_location;/* offset of name in the whole item */ + __u16 deh_state; /* whether 1) entry contains stat data (for + future), and 2) whether entry is hidden + (unlinked) */ +}; + +#define DEH_SIZE (sizeof (struct reiserfs_de_head)) + +#define DEH_Statdata (1 << 0) /* not used now */ +#define DEH_Visible (1 << 2) + +#define SD_OFFSET 0 +#define SD_UNIQUENESS 0 +#define DOT_OFFSET 1 +#define DOT_DOT_OFFSET 2 +#define DIRENTRY_UNIQUENESS 500 + +#define V1_TYPE_STAT_DATA 0x0 +#define V1_TYPE_DIRECT 0xffffffff +#define V1_TYPE_INDIRECT 0xfffffffe +#define V1_TYPE_DIRECTORY_MAX 0xfffffffd +#define V2_TYPE_STAT_DATA 0 +#define V2_TYPE_INDIRECT 1 +#define V2_TYPE_DIRECT 2 +#define V2_TYPE_DIRENTRY 3 + +#define REISERFS_ROOT_OBJECTID 2 +#define REISERFS_ROOT_PARENT_OBJECTID 1 +#define REISERFS_DISK_OFFSET_IN_BYTES (64 * 1024) +/* the spot for the super in versions 3.5 - 3.5.11 (inclusive) */ +#define REISERFS_OLD_DISK_OFFSET_IN_BYTES (8 * 1024) +#define REISERFS_OLD_BLOCKSIZE 4096 + +#define S_ISREG(mode) (((mode) & 0170000) == 0100000) +#define S_ISDIR(mode) (((mode) & 0170000) == 0040000) +#define S_ISLNK(mode) (((mode) & 0170000) == 0120000) + +#define PATH_MAX 1024 /* include/linux/limits.h */ +#define MAX_LINK_COUNT 5 /* number of symbolic links to follow */ + +/* The size of the node cache */ +#define FSYSREISER_CACHE_SIZE 24*1024 +#define FSYSREISER_MIN_BLOCKSIZE SECTOR_SIZE +#define FSYSREISER_MAX_BLOCKSIZE FSYSREISER_CACHE_SIZE / 3 + +/* Info about currently opened file */ +struct fsys_reiser_fileinfo +{ + __u32 k_dir_id; + __u32 k_objectid; +}; + +/* In memory info about the currently mounted filesystem */ +struct fsys_reiser_info +{ + /* The last read item head */ + struct item_head *current_ih; + /* The last read item */ + char *current_item; + /* The information for the currently opened file */ + struct fsys_reiser_fileinfo fileinfo; + /* The start of the journal */ + __u32 journal_block; + /* The size of the journal */ + __u32 journal_block_count; + /* The first valid descriptor block in journal + (relative to journal_block) */ + __u32 journal_first_desc; + + /* The ReiserFS version. */ + __u16 version; + /* The current depth of the reiser tree. */ + __u16 tree_depth; + /* SECTOR_SIZE << blocksize_shift == blocksize. */ + __u8 blocksize_shift; + /* 1 << full_blocksize_shift == blocksize. */ + __u8 fullblocksize_shift; + /* The reiserfs block size (must be a power of 2) */ + __u16 blocksize; + /* The number of cached tree nodes */ + __u16 cached_slots; + /* The number of valid transactions in journal */ + __u16 journal_transactions; + + unsigned int blocks[MAX_HEIGHT]; + unsigned int next_key_nr[MAX_HEIGHT]; +}; + +/* The cached s+tree blocks in FSYS_BUF, see below + * for a more detailed description. + */ +#define ROOT ((char *) ((int) FSYS_BUF)) +#define CACHE(i) (ROOT + ((i) << INFO->fullblocksize_shift)) +#define LEAF CACHE (DISK_LEAF_NODE_LEVEL) + +#define BLOCKHEAD(cache) ((struct block_head *) cache) +#define ITEMHEAD ((struct item_head *) ((int) LEAF + BLKH_SIZE)) +#define KEY(cache) ((struct key *) ((int) cache + BLKH_SIZE)) +#define DC(cache) ((struct disk_child *) \ + ((int) cache + BLKH_SIZE + KEY_SIZE * nr_item)) +/* The fsys_reiser_info block. + */ +#define INFO \ + ((struct fsys_reiser_info *) ((int) FSYS_BUF + FSYSREISER_CACHE_SIZE)) +/* + * The journal cache. For each transaction it contains the number of + * blocks followed by the real block numbers of this transaction. + * + * If the block numbers of some transaction won't fit in this space, + * this list is stopped with a 0xffffffff marker and the remaining + * uncommitted transactions aren't cached. + */ +#define JOURNAL_START ((__u32 *) (INFO + 1)) +#define JOURNAL_END ((__u32 *) (FSYS_BUF + FSYS_BUFLEN)) + + +static __inline__ unsigned long +log2 (unsigned long word) +{ + __asm__ ("bsfl %1,%0" + : "=r" (word) + : "r" (word)); + return word; +} + +static __inline__ int +is_power_of_two (unsigned long word) +{ + return (word & -word) == word; +} + +static int +journal_read (int block, int len, char *buffer) +{ + return devread ((INFO->journal_block + block) << INFO->blocksize_shift, + 0, len, buffer); +} + +/* Read a block from ReiserFS file system, taking the journal into + * account. If the block nr is in the journal, the block from the + * journal taken. + */ +static int +block_read (int blockNr, int start, int len, char *buffer) +{ + int transactions = INFO->journal_transactions; + int desc_block = INFO->journal_first_desc; + int journal_mask = INFO->journal_block_count - 1; + int translatedNr = blockNr; + __u32 *journal_table = JOURNAL_START; + while (transactions-- > 0) + { + int i = 0; + int j_len; + if (*journal_table != 0xffffffff) + { + /* Search for the blockNr in cached journal */ + j_len = *journal_table++; + while (i++ < j_len) + { + if (*journal_table++ == blockNr) + { + journal_table += j_len - i; + goto found; + } + } + } + else + { + /* This is the end of cached journal marker. The remaining + * transactions are still on disk. + */ + struct reiserfs_journal_desc desc; + struct reiserfs_journal_commit commit; + + if (! journal_read (desc_block, sizeof (desc), (char *) &desc)) + return 0; + + j_len = desc.j_len; + while (i < j_len && i < JOURNAL_TRANS_HALF) + if (desc.j_realblock[i++] == blockNr) + goto found; + + if (j_len >= JOURNAL_TRANS_HALF) + { + int commit_block = (desc_block + 1 + j_len) & journal_mask; + if (! journal_read (commit_block, + sizeof (commit), (char *) &commit)) + return 0; + while (i < j_len) + if (commit.j_realblock[i++ - JOURNAL_TRANS_HALF] == blockNr) + goto found; + } + } + goto not_found; + + found: + translatedNr = INFO->journal_block + ((desc_block + i) & journal_mask); +#ifdef REISERDEBUG + printf ("block_read: block %d is mapped to journal block %d.\n", + blockNr, translatedNr - INFO->journal_block); +#endif + /* We must continue the search, as this block may be overwritten + * in later transactions. + */ + not_found: + desc_block = (desc_block + 2 + j_len) & journal_mask; + } + return devread (translatedNr << INFO->blocksize_shift, start, len, buffer); +} + +/* Init the journal data structure. We try to cache as much as + * possible in the JOURNAL_START-JOURNAL_END space, but if it is full + * we can still read the rest from the disk on demand. + * + * The first number of valid transactions and the descriptor block of the + * first valid transaction are held in INFO. The transactions are all + * adjacent, but we must take care of the journal wrap around. + */ +static int +journal_init (void) +{ + unsigned int block_count = INFO->journal_block_count; + unsigned int desc_block; + unsigned int commit_block; + unsigned int next_trans_id; + struct reiserfs_journal_header header; + struct reiserfs_journal_desc desc; + struct reiserfs_journal_commit commit; + __u32 *journal_table = JOURNAL_START; + + journal_read (block_count, sizeof (header), (char *) &header); + desc_block = header.j_first_unflushed_offset; + if (desc_block >= block_count) + return 0; + + INFO->journal_first_desc = desc_block; + next_trans_id = header.j_last_flush_trans_id + 1; + +#ifdef REISERDEBUG + printf ("journal_init: last flushed %d\n", + header.j_last_flush_trans_id); +#endif + + while (1) + { + journal_read (desc_block, sizeof (desc), (char *) &desc); + if (substring (JOURNAL_DESC_MAGIC, desc.j_magic) > 0 + || desc.j_trans_id != next_trans_id + || desc.j_mount_id != header.j_mount_id) + /* no more valid transactions */ + break; + + commit_block = (desc_block + desc.j_len + 1) & (block_count - 1); + journal_read (commit_block, sizeof (commit), (char *) &commit); + if (desc.j_trans_id != commit.j_trans_id + || desc.j_len != commit.j_len) + /* no more valid transactions */ + break; + +#ifdef REISERDEBUG + printf ("Found valid transaction %d/%d at %d.\n", + desc.j_trans_id, desc.j_mount_id, desc_block); +#endif + + next_trans_id++; + if (journal_table < JOURNAL_END) + { + if ((journal_table + 1 + desc.j_len) >= JOURNAL_END) + { + /* The table is almost full; mark the end of the cached + * journal.*/ + *journal_table = 0xffffffff; + journal_table = JOURNAL_END; + } + else + { + int i; + /* Cache the length and the realblock numbers in the table. + * The block number of descriptor can easily be computed. + * and need not to be stored here. + */ + *journal_table++ = desc.j_len; + for (i = 0; i < desc.j_len && i < JOURNAL_TRANS_HALF; i++) + { + *journal_table++ = desc.j_realblock[i]; +#ifdef REISERDEBUG + printf ("block %d is in journal %d.\n", + desc.j_realblock[i], desc_block); +#endif + } + for ( ; i < desc.j_len; i++) + { + *journal_table++ = commit.j_realblock[i-JOURNAL_TRANS_HALF]; +#ifdef REISERDEBUG + printf ("block %d is in journal %d.\n", + commit.j_realblock[i-JOURNAL_TRANS_HALF], + desc_block); +#endif + } + } + } + desc_block = (commit_block + 1) & (block_count - 1); + } +#ifdef REISERDEBUG + printf ("Transaction %d/%d at %d isn't valid.\n", + desc.j_trans_id, desc.j_mount_id, desc_block); +#endif + + INFO->journal_transactions + = next_trans_id - header.j_last_flush_trans_id - 1; + return errnum == 0; +} + +/* check filesystem types and read superblock into memory buffer */ +int +reiserfs_mount (void) +{ + struct reiserfs_super_block super; + int superblock = REISERFS_DISK_OFFSET_IN_BYTES >> SECTOR_BITS; + + if (part_length < superblock + (sizeof (super) >> SECTOR_BITS) + || ! devread (superblock, 0, sizeof (struct reiserfs_super_block), + (char *) &super) + || (substring (REISER2FS_SUPER_MAGIC_STRING, super.s_magic) > 0 + && substring (REISERFS_SUPER_MAGIC_STRING, super.s_magic) > 0) + || (/* check that this is not a copy inside the journal log */ + super.s_journal_block * super.s_blocksize + <= REISERFS_DISK_OFFSET_IN_BYTES)) + { + /* Try old super block position */ + superblock = REISERFS_OLD_DISK_OFFSET_IN_BYTES >> SECTOR_BITS; + if (part_length < superblock + (sizeof (super) >> SECTOR_BITS) + || ! devread (superblock, 0, sizeof (struct reiserfs_super_block), + (char *) &super)) + return 0; + + if (substring (REISER2FS_SUPER_MAGIC_STRING, super.s_magic) > 0 + && substring (REISERFS_SUPER_MAGIC_STRING, super.s_magic) > 0) + { + /* pre journaling super block ? */ + if (substring (REISERFS_SUPER_MAGIC_STRING, + (char*) ((int) &super + 20)) > 0) + return 0; + + super.s_blocksize = REISERFS_OLD_BLOCKSIZE; + super.s_journal_block = 0; + super.s_version = 0; + } + } + + /* check the version number. */ + if (super.s_version > REISERFS_MAX_SUPPORTED_VERSION) + return 0; + + INFO->version = super.s_version; + INFO->blocksize = super.s_blocksize; + INFO->fullblocksize_shift = log2 (super.s_blocksize); + INFO->blocksize_shift = INFO->fullblocksize_shift - SECTOR_BITS; + INFO->cached_slots = + (FSYSREISER_CACHE_SIZE >> INFO->fullblocksize_shift) - 1; + +#ifdef REISERDEBUG + printf ("reiserfs_mount: version=%d, blocksize=%d\n", + INFO->version, INFO->blocksize); +#endif /* REISERDEBUG */ + + /* Clear node cache. */ + memset (INFO->blocks, 0, sizeof (INFO->blocks)); + + if (super.s_blocksize < FSYSREISER_MIN_BLOCKSIZE + || super.s_blocksize > FSYSREISER_MAX_BLOCKSIZE + || (SECTOR_SIZE << INFO->blocksize_shift) != super.s_blocksize) + return 0; + + /* Initialize journal code. If something fails we end with zero + * journal_transactions, so we don't access the journal at all. + */ + INFO->journal_transactions = 0; + if (super.s_journal_block != 0 && super.s_journal_dev == 0) + { + INFO->journal_block = super.s_journal_block; + INFO->journal_block_count = super.s_journal_size; + if (is_power_of_two (INFO->journal_block_count)) + journal_init (); + + /* Read in super block again, maybe it is in the journal */ + block_read (superblock >> INFO->blocksize_shift, + 0, sizeof (struct reiserfs_super_block), (char *) &super); + } + + if (! block_read (super.s_root_block, 0, INFO->blocksize, (char*) ROOT)) + return 0; + + INFO->tree_depth = BLOCKHEAD (ROOT)->blk_level; + +#ifdef REISERDEBUG + printf ("root read_in: block=%d, depth=%d\n", + super.s_root_block, INFO->tree_depth); +#endif /* REISERDEBUG */ + + if (INFO->tree_depth >= MAX_HEIGHT) + return 0; + if (INFO->tree_depth == DISK_LEAF_NODE_LEVEL) + { + /* There is only one node in the whole filesystem, + * which is simultanously leaf and root */ + memcpy (LEAF, ROOT, INFO->blocksize); + } + return 1; +} + +/***************** TREE ACCESSING METHODS *****************************/ + +/* I assume you are familiar with the ReiserFS tree, if not go to + * http://www.namesys.com/content_table.html + * + * My tree node cache is organized as following + * 0 ROOT node + * 1 LEAF node (if the ROOT is also a LEAF it is copied here + * 2-n other nodes on current path from bottom to top. + * if there is not enough space in the cache, the top most are + * omitted. + * + * I have only two methods to find a key in the tree: + * search_stat(dir_id, objectid) searches for the stat entry (always + * the first entry) of an object. + * next_key() gets the next key in tree order. + * + * This means, that I can only sequential reads of files are + * efficient, but this really doesn't hurt for grub. + */ + +/* Read in the node at the current path and depth into the node cache. + * You must set INFO->blocks[depth] before. + */ +static char * +read_tree_node (unsigned int blockNr, int depth) +{ + char* cache = CACHE(depth); + int num_cached = INFO->cached_slots; + if (depth < num_cached) + { + /* This is the cached part of the path. Check if same block is + * needed. + */ + if (blockNr == INFO->blocks[depth]) + return cache; + } + else + cache = CACHE(num_cached); + +#ifdef REISERDEBUG + printf (" next read_in: block=%d (depth=%d)\n", + blockNr, depth); +#endif /* REISERDEBUG */ + if (! block_read (blockNr, 0, INFO->blocksize, cache)) + return 0; + /* Make sure it has the right node level */ + if (BLOCKHEAD (cache)->blk_level != depth) + { + errnum = ERR_FSYS_CORRUPT; + return 0; + } + + INFO->blocks[depth] = blockNr; + return cache; +} + +/* Get the next key, i.e. the key following the last retrieved key in + * tree order. INFO->current_ih and + * INFO->current_info are adapted accordingly. */ +static int +next_key (void) +{ + int depth; + struct item_head *ih = INFO->current_ih + 1; + char *cache; + +#ifdef REISERDEBUG + printf ("next_key:\n old ih: key %d:%d:%d:%d version:%d\n", + INFO->current_ih->ih_key.k_dir_id, + INFO->current_ih->ih_key.k_objectid, + INFO->current_ih->ih_key.u.v1.k_offset, + INFO->current_ih->ih_key.u.v1.k_uniqueness, + INFO->current_ih->ih_version); +#endif /* REISERDEBUG */ + + if (ih == &ITEMHEAD[BLOCKHEAD (LEAF)->blk_nr_item]) + { + depth = DISK_LEAF_NODE_LEVEL; + /* The last item, was the last in the leaf node. + * Read in the next block + */ + do + { + if (depth == INFO->tree_depth) + { + /* There are no more keys at all. + * Return a dummy item with MAX_KEY */ + ih = (struct item_head *) &BLOCKHEAD (LEAF)->blk_right_delim_key; + goto found; + } + depth++; +#ifdef REISERDEBUG + printf (" depth=%d, i=%d\n", depth, INFO->next_key_nr[depth]); +#endif /* REISERDEBUG */ + } + while (INFO->next_key_nr[depth] == 0); + + if (depth == INFO->tree_depth) + cache = ROOT; + else if (depth <= INFO->cached_slots) + cache = CACHE (depth); + else + { + cache = read_tree_node (INFO->blocks[depth], depth); + if (! cache) + return 0; + } + + do + { + int nr_item = BLOCKHEAD (cache)->blk_nr_item; + int key_nr = INFO->next_key_nr[depth]++; +#ifdef REISERDEBUG + printf (" depth=%d, i=%d/%d\n", depth, key_nr, nr_item); +#endif /* REISERDEBUG */ + if (key_nr == nr_item) + /* This is the last item in this block, set the next_key_nr to 0 */ + INFO->next_key_nr[depth] = 0; + + cache = read_tree_node (DC (cache)[key_nr].dc_block_number, --depth); + if (! cache) + return 0; + } + while (depth > DISK_LEAF_NODE_LEVEL); + + ih = ITEMHEAD; + } + found: + INFO->current_ih = ih; + INFO->current_item = &LEAF[ih->ih_item_location]; +#ifdef REISERDEBUG + printf (" new ih: key %d:%d:%d:%d version:%d\n", + INFO->current_ih->ih_key.k_dir_id, + INFO->current_ih->ih_key.k_objectid, + INFO->current_ih->ih_key.u.v1.k_offset, + INFO->current_ih->ih_key.u.v1.k_uniqueness, + INFO->current_ih->ih_version); +#endif /* REISERDEBUG */ + return 1; +} + +/* preconditions: reiserfs_mount already executed, therefore + * INFO block is valid + * returns: 0 if error (errnum is set), + * nonzero iff we were able to find the key successfully. + * postconditions: on a nonzero return, the current_ih and + * current_item fields describe the key that equals the + * searched key. INFO->next_key contains the next key after + * the searched key. + * side effects: messes around with the cache. + */ +static int +search_stat (__u32 dir_id, __u32 objectid) +{ + char *cache; + int depth; + int nr_item; + int i; + struct item_head *ih; +#ifdef REISERDEBUG + printf ("search_stat:\n key %d:%d:0:0\n", dir_id, objectid); +#endif /* REISERDEBUG */ + + depth = INFO->tree_depth; + cache = ROOT; + + while (depth > DISK_LEAF_NODE_LEVEL) + { + struct key *key; + nr_item = BLOCKHEAD (cache)->blk_nr_item; + + key = KEY (cache); + + for (i = 0; i < nr_item; i++) + { + if (key->k_dir_id > dir_id + || (key->k_dir_id == dir_id + && (key->k_objectid > objectid + || (key->k_objectid == objectid + && (key->u.v1.k_offset + | key->u.v1.k_uniqueness) > 0)))) + break; + key++; + } + +#ifdef REISERDEBUG + printf (" depth=%d, i=%d/%d\n", depth, i, nr_item); +#endif /* REISERDEBUG */ + INFO->next_key_nr[depth] = (i == nr_item) ? 0 : i+1; + cache = read_tree_node (DC (cache)[i].dc_block_number, --depth); + if (! cache) + return 0; + } + + /* cache == LEAF */ + nr_item = BLOCKHEAD (LEAF)->blk_nr_item; + ih = ITEMHEAD; + for (i = 0; i < nr_item; i++) + { + if (ih->ih_key.k_dir_id == dir_id + && ih->ih_key.k_objectid == objectid + && ih->ih_key.u.v1.k_offset == 0 + && ih->ih_key.u.v1.k_uniqueness == 0) + { +#ifdef REISERDEBUG + printf (" depth=%d, i=%d/%d\n", depth, i, nr_item); +#endif /* REISERDEBUG */ + INFO->current_ih = ih; + INFO->current_item = &LEAF[ih->ih_item_location]; + return 1; + } + ih++; + } + errnum = ERR_FSYS_CORRUPT; + return 0; +} + +int +reiserfs_read (char *buf, int len) +{ + unsigned int blocksize; + unsigned int offset; + unsigned int to_read; + char *prev_buf = buf; + +#ifdef REISERDEBUG + printf ("reiserfs_read: filepos=%d len=%d, offset=%x:%x\n", + filepos, len, (__u64) IH_KEY_OFFSET (INFO->current_ih) - 1); +#endif /* REISERDEBUG */ + + if (INFO->current_ih->ih_key.k_objectid != INFO->fileinfo.k_objectid + || IH_KEY_OFFSET (INFO->current_ih) > filepos + 1) + { + search_stat (INFO->fileinfo.k_dir_id, INFO->fileinfo.k_objectid); + goto get_next_key; + } + + while (! errnum) + { + if (INFO->current_ih->ih_key.k_objectid != INFO->fileinfo.k_objectid) + break; + + offset = filepos - IH_KEY_OFFSET (INFO->current_ih) + 1; + blocksize = INFO->current_ih->ih_item_len; + +#ifdef REISERDEBUG + printf (" loop: filepos=%d len=%d, offset=%d blocksize=%d\n", + filepos, len, offset, blocksize); +#endif /* REISERDEBUG */ + + if (IH_KEY_ISTYPE(INFO->current_ih, TYPE_DIRECT) + && offset < blocksize) + { +#ifdef REISERDEBUG + printf ("direct_read: offset=%d, blocksize=%d\n", + offset, blocksize); +#endif /* REISERDEBUG */ + to_read = blocksize - offset; + if (to_read > len) + to_read = len; + + if (disk_read_hook != NULL) + { + disk_read_func = disk_read_hook; + + block_read (INFO->blocks[DISK_LEAF_NODE_LEVEL], + (INFO->current_item - LEAF + offset), to_read, buf); + + disk_read_func = NULL; + } + else + memcpy (buf, INFO->current_item + offset, to_read); + goto update_buf_len; + } + else if (IH_KEY_ISTYPE(INFO->current_ih, TYPE_INDIRECT)) + { + blocksize = (blocksize >> 2) << INFO->fullblocksize_shift; +#ifdef REISERDEBUG + printf ("indirect_read: offset=%d, blocksize=%d\n", + offset, blocksize); +#endif /* REISERDEBUG */ + + while (offset < blocksize) + { + __u32 blocknr = ((__u32 *) INFO->current_item) + [offset >> INFO->fullblocksize_shift]; + int blk_offset = offset & (INFO->blocksize-1); + + to_read = INFO->blocksize - blk_offset; + if (to_read > len) + to_read = len; + + disk_read_func = disk_read_hook; + + /* Journal is only for meta data. Data blocks can be read + * directly without using block_read + */ + devread (blocknr << INFO->blocksize_shift, + blk_offset, to_read, buf); + + disk_read_func = NULL; + update_buf_len: + len -= to_read; + buf += to_read; + offset += to_read; + filepos += to_read; + if (len == 0) + goto done; + } + } + get_next_key: + next_key (); + } + done: + return errnum ? 0 : buf - prev_buf; +} + + +/* preconditions: reiserfs_mount already executed, therefore + * INFO block is valid + * returns: 0 if error, nonzero iff we were able to find the file successfully + * postconditions: on a nonzero return, INFO->fileinfo contains the info + * of the file we were trying to look up, filepos is 0 and filemax is + * the size of the file. + */ +int +reiserfs_dir (char *dirname) +{ + struct reiserfs_de_head *de_head; + char *rest, ch; + __u32 dir_id, objectid, parent_dir_id = 0, parent_objectid = 0; +#ifndef STAGE1_5 + int do_possibilities = 0; +#endif /* ! STAGE1_5 */ + char linkbuf[PATH_MAX]; /* buffer for following symbolic links */ + int link_count = 0; + int mode; + + dir_id = REISERFS_ROOT_PARENT_OBJECTID; + objectid = REISERFS_ROOT_OBJECTID; + + while (1) + { +#ifdef REISERDEBUG + printf ("dirname=%s\n", dirname); +#endif /* REISERDEBUG */ + + /* Search for the stat info first. */ + if (! search_stat (dir_id, objectid)) + return 0; + +#ifdef REISERDEBUG + printf ("sd_mode=%x sd_size=%d\n", + ((struct stat_data *) INFO->current_item)->sd_mode, + ((struct stat_data *) INFO->current_item)->sd_size); +#endif /* REISERDEBUG */ + + mode = ((struct stat_data *) INFO->current_item)->sd_mode; + + /* If we've got a symbolic link, then chase it. */ + if (S_ISLNK (mode)) + { + int len; + if (++link_count > MAX_LINK_COUNT) + { + errnum = ERR_SYMLINK_LOOP; + return 0; + } + + /* Get the symlink size. */ + filemax = ((struct stat_data *) INFO->current_item)->sd_size; + + /* Find out how long our remaining name is. */ + len = 0; + while (dirname[len] && !isspace (dirname[len])) + len++; + + if (filemax + len > sizeof (linkbuf) - 1) + { + errnum = ERR_FILELENGTH; + return 0; + } + + /* Copy the remaining name to the end of the symlink data. + Note that DIRNAME and LINKBUF may overlap! */ + grub_memmove (linkbuf + filemax, dirname, len+1); + + INFO->fileinfo.k_dir_id = dir_id; + INFO->fileinfo.k_objectid = objectid; + filepos = 0; + if (! next_key () + || reiserfs_read (linkbuf, filemax) != filemax) + { + if (! errnum) + errnum = ERR_FSYS_CORRUPT; + return 0; + } + +#ifdef REISERDEBUG + printf ("symlink=%s\n", linkbuf); +#endif /* REISERDEBUG */ + + dirname = linkbuf; + if (*dirname == '/') + { + /* It's an absolute link, so look it up in root. */ + dir_id = REISERFS_ROOT_PARENT_OBJECTID; + objectid = REISERFS_ROOT_OBJECTID; + } + else + { + /* Relative, so look it up in our parent directory. */ + dir_id = parent_dir_id; + objectid = parent_objectid; + } + + /* Now lookup the new name. */ + continue; + } + + /* if we have a real file (and we're not just printing possibilities), + then this is where we want to exit */ + + if (! *dirname || isspace (*dirname)) + { + if (! S_ISREG (mode)) + { + errnum = ERR_BAD_FILETYPE; + return 0; + } + + filepos = 0; + filemax = ((struct stat_data *) INFO->current_item)->sd_size; + + /* If this is a new stat data and size is > 4GB set filemax to + * maximum + */ + if (INFO->current_ih->ih_version == ITEM_VERSION_2 + && ((struct stat_data *) INFO->current_item)->sd_size_hi > 0) + filemax = 0xffffffff; + + INFO->fileinfo.k_dir_id = dir_id; + INFO->fileinfo.k_objectid = objectid; + return next_key (); + } + + /* continue with the file/directory name interpretation */ + while (*dirname == '/') + dirname++; + if (! S_ISDIR (mode)) + { + errnum = ERR_BAD_FILETYPE; + return 0; + } + for (rest = dirname; (ch = *rest) && ! isspace (ch) && ch != '/'; rest++); + *rest = 0; + +# ifndef STAGE1_5 + if (print_possibilities && ch != '/') + do_possibilities = 1; +# endif /* ! STAGE1_5 */ + + while (1) + { + char *name_end; + int num_entries; + + if (! next_key ()) + return 0; +#ifdef REISERDEBUG + printf ("ih: key %d:%d:%d:%d version:%d\n", + INFO->current_ih->ih_key.k_dir_id, + INFO->current_ih->ih_key.k_objectid, + INFO->current_ih->ih_key.u.v1.k_offset, + INFO->current_ih->ih_key.u.v1.k_uniqueness, + INFO->current_ih->ih_version); +#endif /* REISERDEBUG */ + + if (INFO->current_ih->ih_key.k_objectid != objectid) + break; + + name_end = INFO->current_item + INFO->current_ih->ih_item_len; + de_head = (struct reiserfs_de_head *) INFO->current_item; + num_entries = INFO->current_ih->u.ih_entry_count; + while (num_entries > 0) + { + char *filename = INFO->current_item + de_head->deh_location; + char tmp = *name_end; + if ((de_head->deh_state & DEH_Visible)) + { + int cmp; + /* Directory names in ReiserFS are not null + * terminated. We write a temporary 0 behind it. + * NOTE: that this may overwrite the first block in + * the tree cache. That doesn't hurt as long as we + * don't call next_key () in between. + */ + *name_end = 0; + cmp = substring (dirname, filename); + *name_end = tmp; +# ifndef STAGE1_5 + if (do_possibilities) + { + if (cmp <= 0) + { + if (print_possibilities > 0) + print_possibilities = -print_possibilities; + *name_end = 0; + print_a_completion (filename); + *name_end = tmp; + } + } + else +# endif /* ! STAGE1_5 */ + if (cmp == 0) + goto found; + } + /* The beginning of this name marks the end of the next name. + */ + name_end = filename; + de_head++; + num_entries--; + } + } + +# ifndef STAGE1_5 + if (print_possibilities < 0) + return 1; +# endif /* ! STAGE1_5 */ + + errnum = ERR_FILE_NOT_FOUND; + *rest = ch; + return 0; + + found: + + *rest = ch; + dirname = rest; + + parent_dir_id = dir_id; + parent_objectid = objectid; + dir_id = de_head->deh_dir_id; + objectid = de_head->deh_objectid; + + } + + return 1; +} + +int +reiserfs_embed (int *start_sector, int needed_sectors) +{ + struct reiserfs_super_block super; + int num_sectors; + + if (! devread (REISERFS_DISK_OFFSET_IN_BYTES >> SECTOR_BITS, 0, + sizeof (struct reiserfs_super_block), (char *) &super)) + return 0; + + *start_sector = 1; /* reserve first sector for stage1 */ + if ((substring (REISERFS_SUPER_MAGIC_STRING, super.s_magic) <= 0 + || substring (REISER2FS_SUPER_MAGIC_STRING, super.s_magic) <= 0) + && (/* check that this is not a super block copy inside + * the journal log */ + super.s_journal_block * super.s_blocksize + > REISERFS_DISK_OFFSET_IN_BYTES)) + num_sectors = (REISERFS_DISK_OFFSET_IN_BYTES >> SECTOR_BITS) - 1; + else + num_sectors = (REISERFS_OLD_DISK_OFFSET_IN_BYTES >> SECTOR_BITS) - 1; + + return (needed_sectors <= num_sectors); +} +#endif /* FSYS_REISERFS */ diff --git a/src/filo/fs/fsys_xfs.c b/src/filo/fs/fsys_xfs.c new file mode 100644 index 00000000..b551c07a --- /dev/null +++ b/src/filo/fs/fsys_xfs.c @@ -0,0 +1,624 @@ +/* fsys_xfs.c - an implementation for the SGI XFS file system */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2001,2002 Free Software Foundation, Inc. + * + * 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. + */ + +#ifdef FSYS_XFS + +#include "shared.h" +#include "filesys.h" +#include "xfs.h" + +#define MAX_LINK_COUNT 8 + +typedef struct xad { + xfs_fileoff_t offset; + xfs_fsblock_t start; + xfs_filblks_t len; +} xad_t; + +struct xfs_info { + int bsize; + int dirbsize; + int isize; + unsigned int agblocks; + int bdlog; + int blklog; + int inopblog; + int agblklog; + int agnolog; + unsigned int nextents; + xfs_daddr_t next; + xfs_daddr_t daddr; + xfs_dablk_t forw; + xfs_dablk_t dablk; + xfs_bmbt_rec_32_t *xt; + xfs_bmbt_ptr_t ptr0; + int btnode_ptr0_off; + int i8param; + int dirpos; + int dirmax; + int blkoff; + int fpos; + xfs_ino_t rootino; +}; + +static struct xfs_info xfs; + +#define dirbuf ((char *)FSYS_BUF) +#define filebuf ((char *)FSYS_BUF + 4096) +#define inode ((xfs_dinode_t *)((char *)FSYS_BUF + 8192)) +#define icore (inode->di_core) + +#define mask32lo(n) (((__uint32_t)1 << (n)) - 1) + +#define XFS_INO_MASK(k) ((__uint32_t)((1ULL << (k)) - 1)) +#define XFS_INO_OFFSET_BITS xfs.inopblog +#define XFS_INO_AGBNO_BITS xfs.agblklog +#define XFS_INO_AGINO_BITS (xfs.agblklog + xfs.inopblog) +#define XFS_INO_AGNO_BITS xfs.agnolog + +static inline xfs_agblock_t +agino2agbno (xfs_agino_t agino) +{ + return agino >> XFS_INO_OFFSET_BITS; +} + +static inline xfs_agnumber_t +ino2agno (xfs_ino_t ino) +{ + return ino >> XFS_INO_AGINO_BITS; +} + +static inline xfs_agino_t +ino2agino (xfs_ino_t ino) +{ + return ino & XFS_INO_MASK(XFS_INO_AGINO_BITS); +} + +static inline int +ino2offset (xfs_ino_t ino) +{ + return ino & XFS_INO_MASK(XFS_INO_OFFSET_BITS); +} + +static inline __const__ __uint16_t +le16 (__uint16_t x) +{ + __asm__("xchgb %b0,%h0" \ + : "=q" (x) \ + : "0" (x)); \ + return x; +} + +static inline __const__ __uint32_t +le32 (__uint32_t x) +{ +#if 0 + /* 386 doesn't have bswap. */ + __asm__("bswap %0" : "=r" (x) : "0" (x)); +#else + /* This is slower but this works on all x86 architectures. */ + __asm__("xchgb %b0, %h0" \ + "\n\troll $16, %0" \ + "\n\txchgb %b0, %h0" \ + : "=q" (x) : "0" (x)); +#endif + return x; +} + +static inline __const__ __uint64_t +le64 (__uint64_t x) +{ + __uint32_t h = x >> 32; + __uint32_t l = x & ((1ULL<<32)-1); + return (((__uint64_t)le32(l)) << 32) | ((__uint64_t)(le32(h))); +} + + +static xfs_fsblock_t +xt_start (xfs_bmbt_rec_32_t *r) +{ + return (((xfs_fsblock_t)(le32 (r->l1) & mask32lo(9))) << 43) | + (((xfs_fsblock_t)le32 (r->l2)) << 11) | + (((xfs_fsblock_t)le32 (r->l3)) >> 21); +} + +static xfs_fileoff_t +xt_offset (xfs_bmbt_rec_32_t *r) +{ + return (((xfs_fileoff_t)le32 (r->l0) & + mask32lo(31)) << 23) | + (((xfs_fileoff_t)le32 (r->l1)) >> 9); +} + +static xfs_filblks_t +xt_len (xfs_bmbt_rec_32_t *r) +{ + return le32(r->l3) & mask32lo(21); +} + +static inline int +xfs_highbit32(__uint32_t v) +{ + int i; + + if (--v) { + for (i = 0; i < 31; i++, v >>= 1) { + if (v == 0) + return i; + } + } + return 0; +} + +static int +isinxt (xfs_fileoff_t key, xfs_fileoff_t offset, xfs_filblks_t len) +{ + return (key >= offset) ? (key < offset + len ? 1 : 0) : 0; +} + +static xfs_daddr_t +agb2daddr (xfs_agnumber_t agno, xfs_agblock_t agbno) +{ + return ((xfs_fsblock_t)agno*xfs.agblocks + agbno) << xfs.bdlog; +} + +static xfs_daddr_t +fsb2daddr (xfs_fsblock_t fsbno) +{ + return agb2daddr ((xfs_agnumber_t)(fsbno >> xfs.agblklog), + (xfs_agblock_t)(fsbno & mask32lo(xfs.agblklog))); +} + +#undef offsetof +#define offsetof(t,m) ((int)&(((t *)0)->m)) + +static inline int +btroot_maxrecs (void) +{ + int tmp = icore.di_forkoff ? (icore.di_forkoff << 3) : xfs.isize; + + return (tmp - sizeof(xfs_bmdr_block_t) - offsetof(xfs_dinode_t, di_u)) / + (sizeof (xfs_bmbt_key_t) + sizeof (xfs_bmbt_ptr_t)); +} + +static int +di_read (xfs_ino_t ino) +{ + xfs_agino_t agino; + xfs_agnumber_t agno; + xfs_agblock_t agbno; + xfs_daddr_t daddr; + int offset; + + agno = ino2agno (ino); + agino = ino2agino (ino); + agbno = agino2agbno (agino); + offset = ino2offset (ino); + daddr = agb2daddr (agno, agbno); + + devread (daddr, offset*xfs.isize, xfs.isize, (char *)inode); + + xfs.ptr0 = *(xfs_bmbt_ptr_t *) + (inode->di_u.di_c + sizeof(xfs_bmdr_block_t) + + btroot_maxrecs ()*sizeof(xfs_bmbt_key_t)); + + return 1; +} + +static void +init_extents (void) +{ + xfs_bmbt_ptr_t ptr0; + xfs_btree_lblock_t h; + + switch (icore.di_format) { + case XFS_DINODE_FMT_EXTENTS: + xfs.xt = inode->di_u.di_bmx; + xfs.nextents = le32 (icore.di_nextents); + break; + case XFS_DINODE_FMT_BTREE: + ptr0 = xfs.ptr0; + for (;;) { + xfs.daddr = fsb2daddr (le64(ptr0)); + devread (xfs.daddr, 0, + sizeof(xfs_btree_lblock_t), (char *)&h); + if (!h.bb_level) { + xfs.nextents = le16(h.bb_numrecs); + xfs.next = fsb2daddr (le64(h.bb_rightsib)); + xfs.fpos = sizeof(xfs_btree_block_t); + return; + } + devread (xfs.daddr, xfs.btnode_ptr0_off, + sizeof(xfs_bmbt_ptr_t), (char *)&ptr0); + } + } +} + +static xad_t * +next_extent (void) +{ + static xad_t xad; + + switch (icore.di_format) { + case XFS_DINODE_FMT_EXTENTS: + if (xfs.nextents == 0) + return NULL; + break; + case XFS_DINODE_FMT_BTREE: + if (xfs.nextents == 0) { + xfs_btree_lblock_t h; + if (xfs.next == 0) + return NULL; + xfs.daddr = xfs.next; + devread (xfs.daddr, 0, sizeof(xfs_btree_lblock_t), (char *)&h); + xfs.nextents = le16(h.bb_numrecs); + xfs.next = fsb2daddr (le64(h.bb_rightsib)); + xfs.fpos = sizeof(xfs_btree_block_t); + } + /* Yeah, I know that's slow, but I really don't care */ + devread (xfs.daddr, xfs.fpos, sizeof(xfs_bmbt_rec_t), filebuf); + xfs.xt = (xfs_bmbt_rec_32_t *)filebuf; + xfs.fpos += sizeof(xfs_bmbt_rec_32_t); + } + xad.offset = xt_offset (xfs.xt); + xad.start = xt_start (xfs.xt); + xad.len = xt_len (xfs.xt); + ++xfs.xt; + --xfs.nextents; + + return &xad; +} + +/* + * Name lies - the function reads only first 100 bytes + */ +static void +xfs_dabread (void) +{ + xad_t *xad; + xfs_fileoff_t offset;; + + init_extents (); + while ((xad = next_extent ())) { + offset = xad->offset; + if (isinxt (xfs.dablk, offset, xad->len)) { + devread (fsb2daddr (xad->start + xfs.dablk - offset), + 0, 100, dirbuf); + break; + } + } +} + +static inline xfs_ino_t +sf_ino (char *sfe, int namelen) +{ + void *p = sfe + namelen + 3; + + return (xfs.i8param == 0) + ? le64(*(xfs_ino_t *)p) : le32(*(__uint32_t *)p); +} + +static inline xfs_ino_t +sf_parent_ino (void) +{ + return (xfs.i8param == 0) + ? le64(*(xfs_ino_t *)(&inode->di_u.di_dir2sf.hdr.parent)) + : le32(*(__uint32_t *)(&inode->di_u.di_dir2sf.hdr.parent)); +} + +static inline int +roundup8 (int n) +{ + return ((n+7)&~7); +} + +static char * +next_dentry (xfs_ino_t *ino) +{ + int namelen = 1; + int toread; + static char *usual[2] = {".", ".."}; + static xfs_dir2_sf_entry_t *sfe; + char *name = usual[0]; + + if (xfs.dirpos >= xfs.dirmax) { + if (xfs.forw == 0) + return NULL; + xfs.dablk = xfs.forw; + xfs_dabread (); +#define h ((xfs_dir2_leaf_hdr_t *)dirbuf) + xfs.dirmax = le16 (h->count) - le16 (h->stale); + xfs.forw = le32 (h->info.forw); +#undef h + xfs.dirpos = 0; + } + + switch (icore.di_format) { + case XFS_DINODE_FMT_LOCAL: + switch (xfs.dirpos) { + case -2: + *ino = 0; + break; + case -1: + *ino = sf_parent_ino (); + ++name; + ++namelen; + sfe = (xfs_dir2_sf_entry_t *) + (inode->di_u.di_c + + sizeof(xfs_dir2_sf_hdr_t) + - xfs.i8param); + break; + default: + namelen = sfe->namelen; + *ino = sf_ino ((char *)sfe, namelen); + name = sfe->name; + sfe = (xfs_dir2_sf_entry_t *) + ((char *)sfe + namelen + 11 - xfs.i8param); + } + break; + case XFS_DINODE_FMT_BTREE: + case XFS_DINODE_FMT_EXTENTS: +#define dau ((xfs_dir2_data_union_t *)dirbuf) + for (;;) { + if (xfs.blkoff >= xfs.dirbsize) { + xfs.blkoff = sizeof(xfs_dir2_data_hdr_t); + filepos &= ~(xfs.dirbsize - 1); + filepos |= xfs.blkoff; + } + xfs_read (dirbuf, 4); + xfs.blkoff += 4; + if (dau->unused.freetag == XFS_DIR2_DATA_FREE_TAG) { + toread = roundup8 (le16(dau->unused.length)) - 4; + xfs.blkoff += toread; + filepos += toread; + continue; + } + break; + } + xfs_read ((char *)dirbuf + 4, 5); + *ino = le64 (dau->entry.inumber); + namelen = dau->entry.namelen; +#undef dau + toread = roundup8 (namelen + 11) - 9; + xfs_read (dirbuf, toread); + name = (char *)dirbuf; + xfs.blkoff += toread + 5; + } + ++xfs.dirpos; + name[namelen] = 0; + + return name; +} + +static char * +first_dentry (xfs_ino_t *ino) +{ + xfs.forw = 0; + switch (icore.di_format) { + case XFS_DINODE_FMT_LOCAL: + xfs.dirmax = inode->di_u.di_dir2sf.hdr.count; + xfs.i8param = inode->di_u.di_dir2sf.hdr.i8count ? 0 : 4; + xfs.dirpos = -2; + break; + case XFS_DINODE_FMT_EXTENTS: + case XFS_DINODE_FMT_BTREE: + filepos = 0; + xfs_read (dirbuf, sizeof(xfs_dir2_data_hdr_t)); + if (((xfs_dir2_data_hdr_t *)dirbuf)->magic == le32(XFS_DIR2_BLOCK_MAGIC)) { +#define tail ((xfs_dir2_block_tail_t *)dirbuf) + filepos = xfs.dirbsize - sizeof(*tail); + xfs_read (dirbuf, sizeof(*tail)); + xfs.dirmax = le32 (tail->count) - le32 (tail->stale); +#undef tail + } else { + xfs.dablk = (1ULL << 35) >> xfs.blklog; +#define h ((xfs_dir2_leaf_hdr_t *)dirbuf) +#define n ((xfs_da_intnode_t *)dirbuf) + for (;;) { + xfs_dabread (); + if ((n->hdr.info.magic == le16(XFS_DIR2_LEAFN_MAGIC)) + || (n->hdr.info.magic == le16(XFS_DIR2_LEAF1_MAGIC))) { + xfs.dirmax = le16 (h->count) - le16 (h->stale); + xfs.forw = le32 (h->info.forw); + break; + } + xfs.dablk = le32 (n->btree[0].before); + } +#undef n +#undef h + } + xfs.blkoff = sizeof(xfs_dir2_data_hdr_t); + filepos = xfs.blkoff; + xfs.dirpos = 0; + } + return next_dentry (ino); +} + +int +xfs_mount (void) +{ + xfs_sb_t super; + + if (!devread (0, 0, sizeof(super), (char *)&super) + || (le32(super.sb_magicnum) != XFS_SB_MAGIC) + || ((le16(super.sb_versionnum) + & XFS_SB_VERSION_NUMBITS) != XFS_SB_VERSION_4) ) { + return 0; + } + + xfs.bsize = le32 (super.sb_blocksize); + xfs.blklog = super.sb_blocklog; + xfs.bdlog = xfs.blklog - SECTOR_BITS; + xfs.rootino = le64 (super.sb_rootino); + xfs.isize = le16 (super.sb_inodesize); + xfs.agblocks = le32 (super.sb_agblocks); + xfs.dirbsize = xfs.bsize << super.sb_dirblklog; + + xfs.inopblog = super.sb_inopblog; + xfs.agblklog = super.sb_agblklog; + xfs.agnolog = xfs_highbit32 (le32(super.sb_agcount)); + + xfs.btnode_ptr0_off = + ((xfs.bsize - sizeof(xfs_btree_block_t)) / + (sizeof (xfs_bmbt_key_t) + sizeof (xfs_bmbt_ptr_t))) + * sizeof(xfs_bmbt_key_t) + sizeof(xfs_btree_block_t); + + return 1; +} + +int +xfs_read (char *buf, int len) +{ + xad_t *xad; + xfs_fileoff_t endofprev, endofcur, offset; + xfs_filblks_t xadlen; + int toread, startpos, endpos; + + if (icore.di_format == XFS_DINODE_FMT_LOCAL) { + grub_memmove (buf, inode->di_u.di_c + filepos, len); + filepos += len; + return len; + } + + startpos = filepos; + endpos = filepos + len; + endofprev = (xfs_fileoff_t)-1; + init_extents (); + while (len > 0 && (xad = next_extent ())) { + offset = xad->offset; + xadlen = xad->len; + if (isinxt (filepos >> xfs.blklog, offset, xadlen)) { + endofcur = (offset + xadlen) << xfs.blklog; + toread = (endofcur >= endpos) + ? len : (endofcur - filepos); + + disk_read_func = disk_read_hook; + devread (fsb2daddr (xad->start), + filepos - (offset << xfs.blklog), toread, buf); + disk_read_func = NULL; + + buf += toread; + len -= toread; + filepos += toread; + } else if (offset > endofprev) { + toread = ((offset << xfs.blklog) >= endpos) + ? len : ((offset - endofprev) << xfs.blklog); + len -= toread; + filepos += toread; + for (; toread; toread--) { + *buf++ = 0; + } + continue; + } + endofprev = offset + xadlen; + } + + return filepos - startpos; +} + +int +xfs_dir (char *dirname) +{ + xfs_ino_t ino, parent_ino, new_ino; + xfs_fsize_t di_size; + int di_mode; + int cmp, n, link_count; + char linkbuf[xfs.bsize]; + char *rest, *name, ch; + + parent_ino = ino = xfs.rootino; + link_count = 0; + for (;;) { + di_read (ino); + di_size = le64 (icore.di_size); + di_mode = le16 (icore.di_mode); + + if ((di_mode & IFMT) == IFLNK) { + if (++link_count > MAX_LINK_COUNT) { + errnum = ERR_SYMLINK_LOOP; + return 0; + } + if (di_size < xfs.bsize - 1) { + filepos = 0; + filemax = di_size; + n = xfs_read (linkbuf, filemax); + } else { + errnum = ERR_FILELENGTH; + return 0; + } + + ino = (linkbuf[0] == '/') ? xfs.rootino : parent_ino; + while (n < (xfs.bsize - 1) && (linkbuf[n++] = *dirname++)); + linkbuf[n] = 0; + dirname = linkbuf; + continue; + } + + if (!*dirname || isspace (*dirname)) { + if ((di_mode & IFMT) != IFREG) { + errnum = ERR_BAD_FILETYPE; + return 0; + } + filepos = 0; + filemax = di_size; + return 1; + } + + if ((di_mode & IFMT) != IFDIR) { + errnum = ERR_BAD_FILETYPE; + return 0; + } + + for (; *dirname == '/'; dirname++); + + for (rest = dirname; (ch = *rest) && !isspace (ch) && ch != '/'; rest++); + *rest = 0; + + name = first_dentry (&new_ino); + for (;;) { + cmp = (!*dirname) ? -1 : substring (dirname, name); +#ifndef STAGE1_5 + if (print_possibilities && ch != '/' && cmp <= 0) { + if (print_possibilities > 0) + print_possibilities = -print_possibilities; + print_a_completion (name); + } else +#endif + if (cmp == 0) { + parent_ino = ino; + if (new_ino) + ino = new_ino; + *(dirname = rest) = ch; + break; + } + name = next_dentry (&new_ino); + if (name == NULL) { + if (print_possibilities < 0) + return 1; + + errnum = ERR_FILE_NOT_FOUND; + *rest = ch; + return 0; + } + } + } +} + +#endif /* FSYS_XFS */ diff --git a/src/filo/fs/iso9660.h b/src/filo/fs/iso9660.h new file mode 100644 index 00000000..06a79062 --- /dev/null +++ b/src/filo/fs/iso9660.h @@ -0,0 +1,168 @@ +/* + * ISO 9660 filesystem backend for GRUB (GRand Unified Bootloader) + * including Rock Ridge Extensions support + * + * Copyright (C) 1998, 1999 Kousuke Takai + * + * 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. + */ +/* + * References: + * linux/fs/isofs/rock.[ch] + * mkisofs-1.11.1/diag/isoinfo.c + * mkisofs-1.11.1/iso9660.h + * (all are written by Eric Youngdale) + */ + +/* + * Modified by SONE Takeshi to work with FILO + */ + +#ifndef _ISO9660_H_ +#define _ISO9660_H_ + +#define ISO_SECTOR_BITS (11) +#define ISO_SECTOR_SIZE (1<= file system block size >= PBSIZE >= DISIZE + */ +#define PBSIZE 512 /* physical block size (in byte) */ +#define DISIZE 512 /* on-disk inode size (in byte) */ +#define L2DISIZE 9 +#define INOSPERIAG 4096 /* number of disk inodes per iag */ +#define L2INOSPERIAG 12 +#define INOSPEREXT 32 /* number of disk inode per extent */ +#define L2INOSPEREXT 5 + +/* Minimum number of bytes supported for a JFS partition */ +#define MINJFS (0x1000000) + +/* + * fixed byte offset address + */ +#define SUPER1_OFF 0x8000 /* primary superblock */ + +#define AITBL_OFF (SUPER1_OFF + PSIZE + (PSIZE << 1)) + +/* + * fixed reserved inode number + */ +/* aggregate inode */ +#define AGGREGATE_I 1 /* aggregate inode map inode */ +#define FILESYSTEM_I 16 /* 1st/only fileset inode in ait: + * fileset inode map inode + */ + +/* per fileset inode */ +#define ROOT_I 2 /* fileset root inode */ + +/* + * directory configuration + */ +#define JFS_NAME_MAX 255 +#define JFS_PATH_MAX PSIZE + +typedef unsigned char u8; +typedef char s8; +typedef unsigned short u16; +typedef short s16; +typedef unsigned int u32; +typedef int s32; +typedef unsigned long long u64; +typedef long long s64; + +typedef u16 UniChar; + +/* these from jfs_btree.h */ + +/* btpaget_t flag */ +#define BT_TYPE 0x07 /* B+-tree index */ +#define BT_ROOT 0x01 /* root page */ +#define BT_LEAF 0x02 /* leaf page */ +#define BT_INTERNAL 0x04 /* internal page */ +#define BT_RIGHTMOST 0x10 /* rightmost page */ +#define BT_LEFTMOST 0x20 /* leftmost page */ + +/* those are from jfs_types.h */ + +struct timestruc_t { + u32 tv_sec; + u32 tv_nsec; +}; + +/* + * physical xd (pxd) + */ +typedef struct { + unsigned len:24; + unsigned addr1:8; + u32 addr2; +} pxd_t; + +/* xd_t field extraction */ +#define lengthPXD(pxd) ((pxd)->len) +#define addressPXD(pxd) (((s64)((pxd)->addr1)) << 32 | ((pxd)->addr2)) + +/* + * data extent descriptor (dxd) + */ +typedef struct { + unsigned flag:8; /* 1: flags */ + unsigned rsrvd:24; /* 3: */ + u32 size; /* 4: size in byte */ + unsigned len:24; /* 3: length in unit of fsblksize */ + unsigned addr1:8; /* 1: address in unit of fsblksize */ + u32 addr2; /* 4: address in unit of fsblksize */ +} dxd_t; /* - 16 - */ + +/* + * DASD limit information - stored in directory inode + */ +typedef struct dasd { + u8 thresh; /* Alert Threshold (in percent) */ + u8 delta; /* Alert Threshold delta (in percent) */ + u8 rsrvd1; + u8 limit_hi; /* DASD limit (in logical blocks) */ + u32 limit_lo; /* DASD limit (in logical blocks) */ + u8 rsrvd2[3]; + u8 used_hi; /* DASD usage (in logical blocks) */ + u32 used_lo; /* DASD usage (in logical blocks) */ +} dasd_t; + + +/* from jfs_superblock.h */ + +#define JFS_MAGIC 0x3153464A /* "JFS1" */ + +struct jfs_superblock +{ + u32 s_magic; /* 4: magic number */ + u32 s_version; /* 4: version number */ + + s64 s_size; /* 8: aggregate size in hardware/LVM blocks; + * VFS: number of blocks + */ + s32 s_bsize; /* 4: aggregate block size in bytes; + * VFS: fragment size + */ + s16 s_l2bsize; /* 2: log2 of s_bsize */ + s16 s_l2bfactor; /* 2: log2(s_bsize/hardware block size) */ + s32 s_pbsize; /* 4: hardware/LVM block size in bytes */ + s16 s_l2pbsize; /* 2: log2 of s_pbsize */ + s16 pad; /* 2: padding necessary for alignment */ + + u32 s_agsize; /* 4: allocation group size in aggr. blocks */ + + u32 s_flag; /* 4: aggregate attributes: + * see jfs_filsys.h + */ + u32 s_state; /* 4: mount/unmount/recovery state: + * see jfs_filsys.h + */ + s32 s_compress; /* 4: > 0 if data compression */ + + pxd_t s_ait2; /* 8: first extent of secondary + * aggregate inode table + */ + + pxd_t s_aim2; /* 8: first extent of secondary + * aggregate inode map + */ + u32 s_logdev; /* 4: device address of log */ + s32 s_logserial; /* 4: log serial number at aggregate mount */ + pxd_t s_logpxd; /* 8: inline log extent */ + + pxd_t s_fsckpxd; /* 8: inline fsck work space extent */ + + struct timestruc_t s_time; /* 8: time last updated */ + + s32 s_fsckloglen; /* 4: Number of filesystem blocks reserved for + * the fsck service log. + * N.B. These blocks are divided among the + * versions kept. This is not a per + * version size. + * N.B. These blocks are included in the + * length field of s_fsckpxd. + */ + s8 s_fscklog; /* 1: which fsck service log is most recent + * 0 => no service log data yet + * 1 => the first one + * 2 => the 2nd one + */ + char s_fpack[11]; /* 11: file system volume name + * N.B. This must be 11 bytes to + * conform with the OS/2 BootSector + * requirements + */ + + /* extendfs() parameter under s_state & FM_EXTENDFS */ + s64 s_xsize; /* 8: extendfs s_size */ + pxd_t s_xfsckpxd; /* 8: extendfs fsckpxd */ + pxd_t s_xlogpxd; /* 8: extendfs logpxd */ + /* - 128 byte boundary - */ + + /* + * DFS VFS support (preliminary) + */ + char s_attach; /* 1: VFS: flag: set when aggregate is attached + */ + u8 rsrvd4[7]; /* 7: reserved - set to 0 */ + + u64 totalUsable; /* 8: VFS: total of 1K blocks which are + * available to "normal" (non-root) users. + */ + u64 minFree; /* 8: VFS: # of 1K blocks held in reserve for + * exclusive use of root. This value can be 0, + * and if it is then totalUsable will be equal + * to # of blocks in aggregate. I believe this + * means that minFree + totalUsable = # blocks. + * In that case, we don't need to store both + * totalUsable and minFree since we can compute + * one from the other. I would guess minFree + * would be the one we should store, and + * totalUsable would be the one we should + * compute. (Just a guess...) + */ + + u64 realFree; /* 8: VFS: # of free 1K blocks can be used by + * "normal" users. It may be this is something + * we should compute when asked for instead of + * storing in the superblock. I don't know how + * often this information is needed. + */ + /* + * graffiti area + */ +}; + +/* from jfs_dtree.h */ + +/* + * entry segment/slot + * + * an entry consists of type dependent head/only segment/slot and + * additional segments/slots linked vi next field; + * N.B. last/only segment of entry is terminated by next = -1; + */ +/* + * directory page slot + */ +typedef struct { + s8 next; /* 1: */ + s8 cnt; /* 1: */ + UniChar name[15]; /* 30: */ +} dtslot_t; /* (32) */ + +#define DTSLOTDATALEN 15 + +/* + * internal node entry head/only segment + */ +typedef struct { + pxd_t xd; /* 8: child extent descriptor */ + + s8 next; /* 1: */ + u8 namlen; /* 1: */ + UniChar name[11]; /* 22: 2-byte aligned */ +} idtentry_t; /* (32) */ + +/* + * leaf node entry head/only segment + * + * For legacy filesystems, name contains 13 unichars -- no index field + */ +typedef struct { + u32 inumber; /* 4: 4-byte aligned */ + s8 next; /* 1: */ + u8 namlen; /* 1: */ + UniChar name[11]; /* 22: 2-byte aligned */ + u32 index; /* 4: index into dir_table */ +} ldtentry_t; /* (32) */ + +#define DTLHDRDATALEN 11 + +/* + * dir_table used for directory traversal during readdir +*/ + +/* + * Maximum entry in inline directory table + */ + +typedef struct dir_table_slot { + u8 rsrvd; /* 1: */ + u8 flag; /* 1: 0 if free */ + u8 slot; /* 1: slot within leaf page of entry */ + u8 addr1; /* 1: upper 8 bits of leaf page address */ + u32 addr2; /* 4: lower 32 bits of leaf page address -OR- + index of next entry when this entry was deleted */ +} dir_table_slot_t; /* (8) */ + +/* + * directory root page (in-line in on-disk inode): + * + * cf. dtpage_t below. + */ +typedef union { + struct { + dasd_t DASD; /* 16: DASD limit/usage info F226941 */ + + u8 flag; /* 1: */ + s8 nextindex; /* 1: next free entry in stbl */ + s8 freecnt; /* 1: free count */ + s8 freelist; /* 1: freelist header */ + + u32 idotdot; /* 4: parent inode number */ + + s8 stbl[8]; /* 8: sorted entry index table */ + } header; /* (32) */ + + dtslot_t slot[9]; +} dtroot_t; + +/* + * directory regular page: + * + * entry slot array of 32 byte slot + * + * sorted entry slot index table (stbl): + * contiguous slots at slot specified by stblindex, + * 1-byte per entry + * 512 byte block: 16 entry tbl (1 slot) + * 1024 byte block: 32 entry tbl (1 slot) + * 2048 byte block: 64 entry tbl (2 slot) + * 4096 byte block: 128 entry tbl (4 slot) + * + * data area: + * 512 byte block: 16 - 2 = 14 slot + * 1024 byte block: 32 - 2 = 30 slot + * 2048 byte block: 64 - 3 = 61 slot + * 4096 byte block: 128 - 5 = 123 slot + * + * N.B. index is 0-based; index fields refer to slot index + * except nextindex which refers to entry index in stbl; + * end of entry stot list or freelist is marked with -1. + */ +typedef union { + struct { + s64 next; /* 8: next sibling */ + s64 prev; /* 8: previous sibling */ + + u8 flag; /* 1: */ + s8 nextindex; /* 1: next entry index in stbl */ + s8 freecnt; /* 1: */ + s8 freelist; /* 1: slot index of head of freelist */ + + u8 maxslot; /* 1: number of slots in page slot[] */ + s8 stblindex; /* 1: slot index of start of stbl */ + u8 rsrvd[2]; /* 2: */ + + pxd_t self; /* 8: self pxd */ + } header; /* (32) */ + + dtslot_t slot[128]; +} dtpage_t; + +/* from jfs_xtree.h */ + +/* + * extent allocation descriptor (xad) + */ +typedef struct xad { + unsigned flag:8; /* 1: flag */ + unsigned rsvrd:16; /* 2: reserved */ + unsigned off1:8; /* 1: offset in unit of fsblksize */ + u32 off2; /* 4: offset in unit of fsblksize */ + unsigned len:24; /* 3: length in unit of fsblksize */ + unsigned addr1:8; /* 1: address in unit of fsblksize */ + u32 addr2; /* 4: address in unit of fsblksize */ +} xad_t; /* (16) */ + +/* xad_t field extraction */ +#define offsetXAD(xad) (((s64)((xad)->off1)) << 32 | ((xad)->off2)) +#define addressXAD(xad) (((s64)((xad)->addr1)) << 32 | ((xad)->addr2)) +#define lengthXAD(xad) ((xad)->len) + +/* possible values for maxentry */ +#define XTPAGEMAXSLOT 256 +#define XTENTRYSTART 2 + +/* + * xtree page: + */ +typedef union { + struct xtheader { + s64 next; /* 8: */ + s64 prev; /* 8: */ + + u8 flag; /* 1: */ + u8 rsrvd1; /* 1: */ + s16 nextindex; /* 2: next index = number of entries */ + s16 maxentry; /* 2: max number of entries */ + s16 rsrvd2; /* 2: */ + + pxd_t self; /* 8: self */ + } header; /* (32) */ + + xad_t xad[XTPAGEMAXSLOT]; /* 16 * maxentry: xad array */ +} xtpage_t; + +/* from jfs_dinode.h */ + +struct dinode { + /* + * I. base area (128 bytes) + * ------------------------ + * + * define generic/POSIX attributes + */ + u32 di_inostamp; /* 4: stamp to show inode belongs to fileset */ + s32 di_fileset; /* 4: fileset number */ + u32 di_number; /* 4: inode number, aka file serial number */ + u32 di_gen; /* 4: inode generation number */ + + pxd_t di_ixpxd; /* 8: inode extent descriptor */ + + s64 di_size; /* 8: size */ + s64 di_nblocks; /* 8: number of blocks allocated */ + + u32 di_nlink; /* 4: number of links to the object */ + + u32 di_uid; /* 4: user id of owner */ + u32 di_gid; /* 4: group id of owner */ + + u32 di_mode; /* 4: attribute, format and permission */ + + struct timestruc_t di_atime; /* 8: time last data accessed */ + struct timestruc_t di_ctime; /* 8: time last status changed */ + struct timestruc_t di_mtime; /* 8: time last data modified */ + struct timestruc_t di_otime; /* 8: time created */ + + dxd_t di_acl; /* 16: acl descriptor */ + + dxd_t di_ea; /* 16: ea descriptor */ + + s32 di_next_index; /* 4: Next available dir_table index */ + + s32 di_acltype; /* 4: Type of ACL */ + + /* + * Extension Areas. + * + * Historically, the inode was partitioned into 4 128-byte areas, + * the last 3 being defined as unions which could have multiple + * uses. The first 96 bytes had been completely unused until + * an index table was added to the directory. It is now more + * useful to describe the last 3/4 of the inode as a single + * union. We would probably be better off redesigning the + * entire structure from scratch, but we don't want to break + * commonality with OS/2's JFS at this time. + */ + union { + struct { + /* + * This table contains the information needed to + * find a directory entry from a 32-bit index. + * If the index is small enough, the table is inline, + * otherwise, an x-tree root overlays this table + */ + dir_table_slot_t _table[12]; /* 96: inline */ + + dtroot_t _dtroot; /* 288: dtree root */ + } _dir; /* (384) */ +#define di_dirtable u._dir._table +#define di_dtroot u._dir._dtroot +#define di_parent di_dtroot.header.idotdot +#define di_DASD di_dtroot.header.DASD + + struct { + union { + u8 _data[96]; /* 96: unused */ + struct { + void *_imap; /* 4: unused */ + u32 _gengen; /* 4: generator */ + } _imap; + } _u1; /* 96: */ +#define di_gengen u._file._u1._imap._gengen + + union { + xtpage_t _xtroot; + struct { + u8 unused[16]; /* 16: */ + dxd_t _dxd; /* 16: */ + union { + u32 _rdev; /* 4: */ + u8 _fastsymlink[128]; + } _u; + u8 _inlineea[128]; + } _special; + } _u2; + } _file; +#define di_xtroot u._file._u2._xtroot +#define di_dxd u._file._u2._special._dxd +#define di_btroot di_xtroot +#define di_inlinedata u._file._u2._special._u +#define di_rdev u._file._u2._special._u._rdev +#define di_fastsymlink u._file._u2._special._u._fastsymlink +#define di_inlineea u._file._u2._special._inlineea + } u; +}; + +typedef struct dinode dinode_t; + +/* di_mode */ +#define IFMT 0xF000 /* S_IFMT - mask of file type */ +#define IFDIR 0x4000 /* S_IFDIR - directory */ +#define IFREG 0x8000 /* S_IFREG - regular file */ +#define IFLNK 0xA000 /* S_IFLNK - symbolic link */ + +/* extended mode bits (on-disk inode di_mode) */ +#define INLINEEA 0x00040000 /* inline EA area free */ + +/* from jfs_imap.h */ + +#define EXTSPERIAG 128 /* number of disk inode extent per iag */ +#define SMAPSZ 4 /* number of words per summary map */ +#define MAXAG 128 /* maximum number of allocation groups */ + +/* + * inode allocation map: + * + * inode allocation map consists of + * . the inode map control page and + * . inode allocation group pages (per 4096 inodes) + * which are addressed by standard JFS xtree. + */ +/* + * inode allocation group page (per 4096 inodes of an AG) + */ +typedef struct { + s64 agstart; /* 8: starting block of ag */ + s32 iagnum; /* 4: inode allocation group number */ + s32 inofreefwd; /* 4: ag inode free list forward */ + s32 inofreeback; /* 4: ag inode free list back */ + s32 extfreefwd; /* 4: ag inode extent free list forward */ + s32 extfreeback; /* 4: ag inode extent free list back */ + s32 iagfree; /* 4: iag free list */ + + /* summary map: 1 bit per inode extent */ + s32 inosmap[SMAPSZ]; /* 16: sum map of mapwords w/ free inodes; + * note: this indicates free and backed + * inodes, if the extent is not backed the + * value will be 1. if the extent is + * backed but all inodes are being used the + * value will be 1. if the extent is + * backed but at least one of the inodes is + * free the value will be 0. + */ + s32 extsmap[SMAPSZ]; /* 16: sum map of mapwords w/ free extents */ + s32 nfreeinos; /* 4: number of free inodes */ + s32 nfreeexts; /* 4: number of free extents */ + /* (72) */ + u8 pad[1976]; /* 1976: pad to 2048 bytes */ + /* allocation bit map: 1 bit per inode (0 - free, 1 - allocated) */ + u32 wmap[EXTSPERIAG]; /* 512: working allocation map */ + u32 pmap[EXTSPERIAG]; /* 512: persistent allocation map */ + pxd_t inoext[EXTSPERIAG]; /* 1024: inode extent addresses */ +} iag_t; /* (4096) */ + +#endif /* _JFS_H_ */ diff --git a/src/filo/fs/shared.h b/src/filo/fs/shared.h new file mode 100644 index 00000000..9f2090b3 --- /dev/null +++ b/src/filo/fs/shared.h @@ -0,0 +1 @@ +/* Sorry, nothing is shared here ;) Just for GRUB compatibility. */ diff --git a/src/filo/fs/vfs.c b/src/filo/fs/vfs.c new file mode 100644 index 00000000..9131bf21 --- /dev/null +++ b/src/filo/fs/vfs.c @@ -0,0 +1,193 @@ +/* Interface between GRUB's fs drivers and application code */ +#include + +#include "filesys.h" +#include + +#define DEBUG_THIS DEBUG_VFS +#include + +int filepos; +int filemax; +grub_error_t errnum; +void (*disk_read_hook) (int, int, int); +void (*disk_read_func) (int, int, int); +char FSYS_BUF[FSYS_BUFLEN]; +int fsmax; + +struct fsys_entry { + char *name; + int (*mount_func) (void); + int (*read_func) (char *buf, int len); + int (*dir_func) (char *dirname); + void (*close_func) (void); + int (*embed_func) (int *start_sector, int needed_sectors); +}; + +struct fsys_entry fsys_table[] = { +# ifdef FSYS_FAT + {"fat", fat_mount, fat_read, fat_dir, 0, 0}, +# endif +# ifdef FSYS_EXT2FS + {"ext2fs", ext2fs_mount, ext2fs_read, ext2fs_dir, 0, 0}, +# endif +# ifdef FSYS_MINIX + {"minix", minix_mount, minix_read, minix_dir, 0, 0}, +# endif +# ifdef FSYS_REISERFS + {"reiserfs", reiserfs_mount, reiserfs_read, reiserfs_dir, 0, + reiserfs_embed}, +# endif +# ifdef FSYS_JFS + {"jfs", jfs_mount, jfs_read, jfs_dir, 0, jfs_embed}, +# endif +# ifdef FSYS_XFS + {"xfs", xfs_mount, xfs_read, xfs_dir, 0, 0}, +# endif +# ifdef FSYS_ISO9660 + {"iso9660", iso9660_mount, iso9660_read, iso9660_dir, 0, 0}, +# endif +}; + +/* NULLFS is used to read images from raw device */ +static int nullfs_dir(char *name) +{ + uint64_t dev_size; + + if (name) { + debug("can't have a named file\n"); + return 0; + } + + dev_size = (uint64_t) part_length << 9; + /* GRUB code doesn't like 2GB or bigger files */ + if (dev_size > 0x7fffffff) + dev_size = 0x7fffffff; + filemax = dev_size; + return 1; +} + +static int nullfs_read(char *buf, int len) +{ + if (devread(filepos>>9, filepos&0x1ff, len, buf)) { + filepos += len; + return len; + } else + return 0; +} + +static struct fsys_entry nullfs = + {"nullfs", 0, nullfs_read, nullfs_dir, 0, 0}; + +static struct fsys_entry *fsys; + +int mount_fs(void) +{ + int i; + + for (i = 0; i < sizeof(fsys_table)/sizeof(fsys_table[0]); i++) { + if (fsys_table[i].mount_func()) { + fsys = &fsys_table[i]; + printf("Mounted %s\n", fsys->name); + return 1; + } + } + fsys = 0; + printf("Unknown filesystem type\n"); + return 0; +} + +int file_open(const char *filename) +{ + + char dev[32]; +// char *dev=0; + const char *path; + int len; + int retval = 0; + int reopen; + + path = strchr(filename, ':'); + if (path) { + len = path - filename; + path++; + //dev = malloc(len + 1); + memcpy(dev, filename, len); + dev[len] = '\0'; + } else { + /* No colon is given. Is this device or filename? */ + if (filename[0] == '/') { + /* Anything starts with '/' must be a filename */ + // dev = 0; + dev[0]=0; + + path = filename; + } else { + memcpy(dev, filename, 32); +// dev = strdup(filename); + path = 0; + } + } + debug("dev=%s, path=%s\n", dev, path); + + if (dev && dev[0]) { + if (!devopen(dev, &reopen)) { + fsys = 0; + goto out; + } + if (!reopen) + fsys = 0; + } + + if (path) { + if (!fsys || fsys==&nullfs) { + if (!mount_fs()) + goto out; + } + using_devsize = 0; + if (!path[0]) { + printf("No filename is given\n"); + goto out; + } + } else + fsys = &nullfs; + + filepos = 0; + errnum = 0; + if (!fsys->dir_func((char *) path)) { + printf("errnum=%d\n",errnum); +// printf("File not found\n"); + goto out; + } + retval = 1; +out: +// if (dev) +// free(dev); + return retval; +} + +int file_read(void *buf, unsigned long len) +{ + if (filepos < 0 || filepos > filemax) + filepos = filemax; + if (len < 0 || len > filemax-filepos) + len = filemax - filepos; + errnum = 0; + return fsys->read_func(buf, len); +} + +int file_seek(unsigned long offset) +{ + filepos = offset; + return filepos; +} + +unsigned long file_size(void) +{ + return filemax; +} + +void file_close(void) +{ +} + diff --git a/src/filo/fs/xfs.h b/src/filo/fs/xfs.h new file mode 100644 index 00000000..6a2f8fe0 --- /dev/null +++ b/src/filo/fs/xfs.h @@ -0,0 +1,546 @@ +/* xfs.h - an extraction from xfsprogs-1.3.5/include/xfs* into one file */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (C) 2001 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ + */ + +#ifndef _BITS_TYPES_H +typedef signed char __int8_t; +typedef unsigned char __uint8_t; +typedef short __int16_t; +typedef unsigned short __uint16_t; +typedef int __int32_t; +typedef unsigned int __uint32_t; +typedef long long __int64_t; +typedef unsigned long long __uint64_t; +#endif + +typedef __uint64_t xfs_ino_t; +typedef __uint32_t xfs_agino_t; +typedef __int64_t xfs_daddr_t; +typedef __int64_t xfs_off_t; +typedef __uint8_t uuid_t[16]; + + +/* those are from xfs_types.h */ + +typedef __uint32_t xfs_agblock_t; /* blockno in alloc. group */ +typedef __uint32_t xfs_extlen_t; /* extent length in blocks */ +typedef __uint32_t xfs_agnumber_t; /* allocation group number */ +typedef __int32_t xfs_extnum_t; /* # of extents in a file */ +typedef __int16_t xfs_aextnum_t; /* # extents in an attribute fork */ +typedef __int64_t xfs_fsize_t; /* bytes in a file */ + +typedef __uint32_t xfs_dablk_t; /* dir/attr block number (in file) */ +typedef __uint32_t xfs_dahash_t; /* dir/attr hash value */ + +/* + * Disk based types: + */ +typedef __uint64_t xfs_dfsbno_t; /* blockno in filesystem (agno|agbno) */ +typedef __uint64_t xfs_drfsbno_t; /* blockno in filesystem (raw) */ +typedef __uint64_t xfs_drtbno_t; /* extent (block) in realtime area */ +typedef __uint64_t xfs_dfiloff_t; /* block number in a file */ + +typedef __uint64_t xfs_fsblock_t; /* blockno in filesystem (agno|agbno) */ +typedef __uint64_t xfs_fileoff_t; /* block number in a file */ +typedef __uint64_t xfs_filblks_t; /* number of blocks in a file */ + + +/* those are from xfs_sb.h */ + +#define XFS_SB_MAGIC 0x58465342 /* 'XFSB'*/ +#define XFS_SB_VERSION_4 4 /* 6.2+ - bitmask version */ +#define XFS_SB_VERSION_NUMBITS 0x000f + +typedef struct xfs_sb +{ + __uint32_t sb_magicnum; /* magic number == XFS_SB_MAGIC */ + __uint32_t sb_blocksize; /* logical block size, bytes */ + xfs_drfsbno_t sb_dblocks; /* number of data blocks */ + xfs_drfsbno_t sb_rblocks; /* number of realtime blocks */ + xfs_drtbno_t sb_rextents; /* number of realtime extents */ + uuid_t sb_uuid; /* file system unique id */ + xfs_dfsbno_t sb_logstart; /* starting block of log if internal */ + xfs_ino_t sb_rootino; /* root inode number */ + xfs_ino_t sb_rbmino; /* bitmap inode for realtime extents */ + xfs_ino_t sb_rsumino; /* summary inode for rt bitmap */ + xfs_agblock_t sb_rextsize; /* realtime extent size, blocks */ + xfs_agblock_t sb_agblocks; /* size of an allocation group */ + xfs_agnumber_t sb_agcount; /* number of allocation groups */ + xfs_extlen_t sb_rbmblocks; /* number of rt bitmap blocks */ + xfs_extlen_t sb_logblocks; /* number of log blocks */ + __uint16_t sb_versionnum; /* header version == XFS_SB_VERSION */ + __uint16_t sb_sectsize; /* volume sector size, bytes */ + __uint16_t sb_inodesize; /* inode size, bytes */ + __uint16_t sb_inopblock; /* inodes per block */ + char sb_fname[12]; /* file system name */ + __uint8_t sb_blocklog; /* log2 of sb_blocksize */ + __uint8_t sb_sectlog; /* log2 of sb_sectsize */ + __uint8_t sb_inodelog; /* log2 of sb_inodesize */ + __uint8_t sb_inopblog; /* log2 of sb_inopblock */ + __uint8_t sb_agblklog; /* log2 of sb_agblocks (rounded up) */ + __uint8_t sb_rextslog; /* log2 of sb_rextents */ + __uint8_t sb_inprogress; /* mkfs is in progress, don't mount */ + __uint8_t sb_imax_pct; /* max % of fs for inode space */ + /* statistics */ + /* + * These fields must remain contiguous. If you really + * want to change their layout, make sure you fix the + * code in xfs_trans_apply_sb_deltas(). + */ + __uint64_t sb_icount; /* allocated inodes */ + __uint64_t sb_ifree; /* free inodes */ + __uint64_t sb_fdblocks; /* free data blocks */ + __uint64_t sb_frextents; /* free realtime extents */ + /* + * End contiguous fields. + */ + xfs_ino_t sb_uquotino; /* user quota inode */ + xfs_ino_t sb_gquotino; /* group quota inode */ + __uint16_t sb_qflags; /* quota flags */ + __uint8_t sb_flags; /* misc. flags */ + __uint8_t sb_shared_vn; /* shared version number */ + xfs_extlen_t sb_inoalignmt; /* inode chunk alignment, fsblocks */ + __uint32_t sb_unit; /* stripe or raid unit */ + __uint32_t sb_width; /* stripe or raid width */ + __uint8_t sb_dirblklog; /* log2 of dir block size (fsbs) */ + __uint8_t sb_dummy[7]; /* padding */ +} xfs_sb_t; + + +/* those are from xfs_btree.h */ + +/* + * Long form header: bmap btrees. + */ +typedef struct xfs_btree_lblock +{ + __uint32_t bb_magic; /* magic number for block type */ + __uint16_t bb_level; /* 0 is a leaf */ + __uint16_t bb_numrecs; /* current # of data records */ + xfs_dfsbno_t bb_leftsib; /* left sibling block or NULLDFSBNO */ + xfs_dfsbno_t bb_rightsib; /* right sibling block or NULLDFSBNO */ +} xfs_btree_lblock_t; + +/* + * Combined header and structure, used by common code. + */ +typedef struct xfs_btree_hdr +{ + __uint32_t bb_magic; /* magic number for block type */ + __uint16_t bb_level; /* 0 is a leaf */ + __uint16_t bb_numrecs; /* current # of data records */ +} xfs_btree_hdr_t; + +typedef struct xfs_btree_block +{ + xfs_btree_hdr_t bb_h; /* header */ + union { + struct { + xfs_agblock_t bb_leftsib; + xfs_agblock_t bb_rightsib; + } s; /* short form pointers */ + struct { + xfs_dfsbno_t bb_leftsib; + xfs_dfsbno_t bb_rightsib; + } l; /* long form pointers */ + } bb_u; /* rest */ +} xfs_btree_block_t; + +/* those are from xfs_bmap_btree.h */ + +/* + * Bmap root header, on-disk form only. + */ +typedef struct xfs_bmdr_block +{ + __uint16_t bb_level; /* 0 is a leaf */ + __uint16_t bb_numrecs; /* current # of data records */ +} xfs_bmdr_block_t; + +/* + * Bmap btree record and extent descriptor. + * For 32-bit kernels, + * l0:31 is an extent flag (value 1 indicates non-normal). + * l0:0-30 and l1:9-31 are startoff. + * l1:0-8, l2:0-31, and l3:21-31 are startblock. + * l3:0-20 are blockcount. + * For 64-bit kernels, + * l0:63 is an extent flag (value 1 indicates non-normal). + * l0:9-62 are startoff. + * l0:0-8 and l1:21-63 are startblock. + * l1:0-20 are blockcount. + */ + +#define BMBT_USE_64 1 + +typedef struct xfs_bmbt_rec_32 +{ + __uint32_t l0, l1, l2, l3; +} xfs_bmbt_rec_32_t; +typedef struct xfs_bmbt_rec_64 +{ + __uint64_t l0, l1; +} xfs_bmbt_rec_64_t; + +#if BMBT_USE_64 +typedef __uint64_t xfs_bmbt_rec_base_t; /* use this for casts */ +typedef xfs_bmbt_rec_64_t xfs_bmbt_rec_t, xfs_bmdr_rec_t; +#else /* !BMBT_USE_64 */ +typedef __uint32_t xfs_bmbt_rec_base_t; /* use this for casts */ +typedef xfs_bmbt_rec_32_t xfs_bmbt_rec_t, xfs_bmdr_rec_t; +#endif /* BMBT_USE_64 */ + +/* + * Key structure for non-leaf levels of the tree. + */ +typedef struct xfs_bmbt_key +{ + xfs_dfiloff_t br_startoff; /* starting file offset */ +} xfs_bmbt_key_t, xfs_bmdr_key_t; + +typedef xfs_dfsbno_t xfs_bmbt_ptr_t, xfs_bmdr_ptr_t; /* btree pointer type */ + /* btree block header type */ +typedef struct xfs_btree_lblock xfs_bmbt_block_t; + + +/* those are from xfs_dir2.h */ +/* + * Directory version 2. + * There are 4 possible formats: + * shortform + * single block - data with embedded leaf at the end + * multiple data blocks, single leaf+freeindex block + * data blocks, node&leaf blocks (btree), freeindex blocks + * + * The shortform format is in xfs_dir2_sf.h. + * The single block format is in xfs_dir2_block.h. + * The data block format is in xfs_dir2_data.h. + * The leaf and freeindex block formats are in xfs_dir2_leaf.h. + * Node blocks are the same as the other version, in xfs_da_btree.h. + */ + +/* + * Byte offset in data block and shortform entry. + */ +typedef __uint16_t xfs_dir2_data_off_t; + +/* + * Byte offset in a directory. + */ +typedef xfs_off_t xfs_dir2_off_t; + +/* those are from xfs_da_btree.h */ +/*======================================================================== + * Directory Structure when greater than XFS_LBSIZE(mp) bytes. + *========================================================================*/ + +/* + * This structure is common to both leaf nodes and non-leaf nodes in the Btree. + * + * Is is used to manage a doubly linked list of all blocks at the same + * level in the Btree, and to identify which type of block this is. + */ +#define XFS_DIR2_LEAF1_MAGIC 0xd2f1 /* magic number: v2 dirlf single blks */ +#define XFS_DIR2_LEAFN_MAGIC 0xd2ff /* magic number: v2 dirlf multi blks */ + +typedef struct xfs_da_blkinfo { + xfs_dablk_t forw; /* previous block in list */ + xfs_dablk_t back; /* following block in list */ + __uint16_t magic; /* validity check on block */ + __uint16_t pad; /* unused */ +} xfs_da_blkinfo_t; + +/* + * This is the structure of the root and intermediate nodes in the Btree. + * The leaf nodes are defined above. + * + * Entries are not packed. + * + * Since we have duplicate keys, use a binary search but always follow + * all match in the block, not just the first match found. + */ + +typedef struct xfs_da_intnode { + struct xfs_da_node_hdr { /* constant-structure header block */ + xfs_da_blkinfo_t info; /* block type, links, etc. */ + __uint16_t count; /* count of active entries */ + __uint16_t level; /* level above leaves (leaf == 0) */ + } hdr; + struct xfs_da_node_entry { + xfs_dahash_t hashval; /* hash value for this descendant */ + xfs_dablk_t before; /* Btree block before this key */ + } btree[1]; /* variable sized array of keys */ +} xfs_da_intnode_t; + + +/* those are from xfs_dir2_data.h */ +/* + * Directory format 2, data block structures. + */ + +/* + * Constants. + */ +#define XFS_DIR2_DATA_FREE_TAG 0xffff +#define XFS_DIR2_DATA_FD_COUNT 3 + +/* + * Structures. + */ + +/* + * Describe a free area in the data block. + * The freespace will be formatted as a xfs_dir2_data_unused_t. + */ +typedef struct xfs_dir2_data_free { + xfs_dir2_data_off_t offset; /* start of freespace */ + xfs_dir2_data_off_t length; /* length of freespace */ +} xfs_dir2_data_free_t; + +/* + * Header for the data blocks. + * Always at the beginning of a directory-sized block. + * The code knows that XFS_DIR2_DATA_FD_COUNT is 3. + */ +typedef struct xfs_dir2_data_hdr { + __uint32_t magic; /* XFS_DIR2_DATA_MAGIC */ + /* or XFS_DIR2_BLOCK_MAGIC */ + xfs_dir2_data_free_t bestfree[XFS_DIR2_DATA_FD_COUNT]; +} xfs_dir2_data_hdr_t; + +/* + * Active entry in a data block. Aligned to 8 bytes. + * Tag appears as the last 2 bytes. + */ +typedef struct xfs_dir2_data_entry { + xfs_ino_t inumber; /* inode number */ + __uint8_t namelen; /* name length */ + __uint8_t name[1]; /* name bytes, no null */ + /* variable offset */ + xfs_dir2_data_off_t tag; /* starting offset of us */ +} xfs_dir2_data_entry_t; + +/* + * Unused entry in a data block. Aligned to 8 bytes. + * Tag appears as the last 2 bytes. + */ +typedef struct xfs_dir2_data_unused { + __uint16_t freetag; /* XFS_DIR2_DATA_FREE_TAG */ + xfs_dir2_data_off_t length; /* total free length */ + /* variable offset */ + xfs_dir2_data_off_t tag; /* starting offset of us */ +} xfs_dir2_data_unused_t; + +typedef union { + xfs_dir2_data_entry_t entry; + xfs_dir2_data_unused_t unused; +} xfs_dir2_data_union_t; + + +/* those are from xfs_dir2_leaf.h */ +/* + * Directory version 2, leaf block structures. + */ + +/* + * Leaf block header. + */ +typedef struct xfs_dir2_leaf_hdr { + xfs_da_blkinfo_t info; /* header for da routines */ + __uint16_t count; /* count of entries */ + __uint16_t stale; /* count of stale entries */ +} xfs_dir2_leaf_hdr_t; + + +/* those are from xfs_dir2_block.h */ +/* + * xfs_dir2_block.h + * Directory version 2, single block format structures + */ + +/* + * The single block format is as follows: + * xfs_dir2_data_hdr_t structure + * xfs_dir2_data_entry_t and xfs_dir2_data_unused_t structures + * xfs_dir2_leaf_entry_t structures + * xfs_dir2_block_tail_t structure + */ + +#define XFS_DIR2_BLOCK_MAGIC 0x58443242 /* XD2B: for one block dirs */ + +typedef struct xfs_dir2_block_tail { + __uint32_t count; /* count of leaf entries */ + __uint32_t stale; /* count of stale lf entries */ +} xfs_dir2_block_tail_t; + + +/* those are from xfs_dir2_sf.h */ + +/* + * Directory layout when stored internal to an inode. + * + * Small directories are packed as tightly as possible so as to + * fit into the literal area of the inode. + */ + +/* + * Inode number stored as 8 8-bit values. + */ +typedef struct { __uint8_t i[8]; } xfs_dir2_ino8_t; + +/* + * Inode number stored as 4 8-bit values. + * Works a lot of the time, when all the inode numbers in a directory + * fit in 32 bits. + */ +typedef struct { __uint8_t i[4]; } xfs_dir2_ino4_t; + +typedef union { + xfs_dir2_ino8_t i8; + xfs_dir2_ino4_t i4; +} xfs_dir2_inou_t; + +/* + * Normalized offset (in a data block) of the entry, really xfs_dir2_data_off_t. + * Only need 16 bits, this is the byte offset into the single block form. + */ +typedef struct { __uint8_t i[2]; } xfs_dir2_sf_off_t; + +/* + * The parent directory has a dedicated field, and the self-pointer must + * be calculated on the fly. + * + * Entries are packed toward the top as tightly as possible. The header + * and the elements must be bcopy()'d out into a work area to get correct + * alignment for the inode number fields. + */ +typedef struct xfs_dir2_sf_hdr { + __uint8_t count; /* count of entries */ + __uint8_t i8count; /* count of 8-byte inode #s */ + xfs_dir2_inou_t parent; /* parent dir inode number */ +} xfs_dir2_sf_hdr_t; + +typedef struct xfs_dir2_sf_entry { + __uint8_t namelen; /* actual name length */ + xfs_dir2_sf_off_t offset; /* saved offset */ + __uint8_t name[1]; /* name, variable size */ + xfs_dir2_inou_t inumber; /* inode number, var. offset */ +} xfs_dir2_sf_entry_t; + +typedef struct xfs_dir2_sf { + xfs_dir2_sf_hdr_t hdr; /* shortform header */ + xfs_dir2_sf_entry_t list[1]; /* shortform entries */ +} xfs_dir2_sf_t; + +/* those are from xfs_dinode.h */ + +#define XFS_DINODE_VERSION_1 1 +#define XFS_DINODE_VERSION_2 2 +#define XFS_DINODE_MAGIC 0x494e /* 'IN' */ + +/* + * Disk inode structure. + * This is just the header; the inode is expanded to fill a variable size + * with the last field expanding. It is split into the core and "other" + * because we only need the core part in the in-core inode. + */ +typedef struct xfs_timestamp { + __int32_t t_sec; /* timestamp seconds */ + __int32_t t_nsec; /* timestamp nanoseconds */ +} xfs_timestamp_t; + +/* + * Note: Coordinate changes to this structure with the XFS_DI_* #defines + * below and the offsets table in xfs_ialloc_log_di(). + */ +typedef struct xfs_dinode_core +{ + __uint16_t di_magic; /* inode magic # = XFS_DINODE_MAGIC */ + __uint16_t di_mode; /* mode and type of file */ + __int8_t di_version; /* inode version */ + __int8_t di_format; /* format of di_c data */ + __uint16_t di_onlink; /* old number of links to file */ + __uint32_t di_uid; /* owner's user id */ + __uint32_t di_gid; /* owner's group id */ + __uint32_t di_nlink; /* number of links to file */ + __uint16_t di_projid; /* owner's project id */ + __uint8_t di_pad[10]; /* unused, zeroed space */ + xfs_timestamp_t di_atime; /* time last accessed */ + xfs_timestamp_t di_mtime; /* time last modified */ + xfs_timestamp_t di_ctime; /* time created/inode modified */ + xfs_fsize_t di_size; /* number of bytes in file */ + xfs_drfsbno_t di_nblocks; /* # of direct & btree blocks used */ + xfs_extlen_t di_extsize; /* basic/minimum extent size for file */ + xfs_extnum_t di_nextents; /* number of extents in data fork */ + xfs_aextnum_t di_anextents; /* number of extents in attribute fork*/ + __uint8_t di_forkoff; /* attr fork offs, <<3 for 64b align */ + __int8_t di_aformat; /* format of attr fork's data */ + __uint32_t di_dmevmask; /* DMIG event mask */ + __uint16_t di_dmstate; /* DMIG state info */ + __uint16_t di_flags; /* random flags, XFS_DIFLAG_... */ + __uint32_t di_gen; /* generation number */ +} xfs_dinode_core_t; + +typedef struct xfs_dinode +{ + xfs_dinode_core_t di_core; + xfs_agino_t di_next_unlinked;/* agi unlinked list ptr */ + union { + xfs_bmdr_block_t di_bmbt; /* btree root block */ + xfs_bmbt_rec_32_t di_bmx[1]; /* extent list */ + xfs_dir2_sf_t di_dir2sf; /* shortform directory v2 */ + char di_c[1]; /* local contents */ + } di_u; +} xfs_dinode_t; + +/* + * Values for di_format + */ +typedef enum xfs_dinode_fmt +{ + XFS_DINODE_FMT_DEV, /* CHR, BLK: di_dev */ + XFS_DINODE_FMT_LOCAL, /* DIR, REG: di_c */ + /* LNK: di_symlink */ + XFS_DINODE_FMT_EXTENTS, /* DIR, REG, LNK: di_bmx */ + XFS_DINODE_FMT_BTREE, /* DIR, REG, LNK: di_bmbt */ + XFS_DINODE_FMT_UUID /* MNT: di_uuid */ +} xfs_dinode_fmt_t; + +/* + * File types (mode field) + */ +#define IFMT 0170000 /* type of file */ +#define IFDIR 0040000 /* directory */ +#define IFREG 0100000 /* regular */ +#define IFLNK 0120000 /* symbolic link */ diff --git a/src/filo/i386/context.c b/src/filo/i386/context.c new file mode 100644 index 00000000..54a15e35 --- /dev/null +++ b/src/filo/i386/context.c @@ -0,0 +1,125 @@ +/* + * context switching + * 2003-10 by SONE Takeshi + */ +#include + +#include "segment.h" +#include "context.h" + +#define MAIN_STACK_SIZE 16384 +#define IMAGE_STACK_SIZE 4096 + +static void start_main(void); /* forward decl. */ +void __exit_context(void); /* assembly routine */ + +/* + * Main context structure + * It is placed at the bottom of our stack, and loaded by assembly routine + * to start us up. + */ +struct context main_ctx __attribute__((section (".initctx"))) = { + .gdt_base = (uint32_t) gdt, + .gdt_limit = GDT_LIMIT, + .cs = FLAT_CS, + .ds = FLAT_DS, + .es = FLAT_DS, + .fs = FLAT_DS, + .gs = FLAT_DS, + .ss = FLAT_DS, + .esp = (uint32_t) ESP_LOC(&main_ctx), + .eip = (uint32_t) start_main, + .return_addr = (uint32_t) __exit_context, +}; + +/* This is used by assembly routine to load/store the context which + * it is to switch/switched. */ +struct context *__context = &main_ctx; + +#if 0 +/* Stack for loaded ELF image */ +static uint8_t image_stack[IMAGE_STACK_SIZE]; +#endif + +/* Pointer to startup context (physical address) */ +unsigned long __boot_ctx; + +/* + * Main starter + * This is the C function that runs first. + */ +static void start_main(void) +{ + int retval; + extern int filo(void); + + /* Save startup context, so we can refer to it later. + * We have to keep it in physical address since we will relocate. */ + __boot_ctx = virt_to_phys(__context); + + /* Start the real fun */ + retval = filo(); + + /* Pass return value to startup context. Bootloader may see it. */ + boot_ctx->eax = retval; + + /* Returning from here should jump to __exit_context */ + __context = boot_ctx; +} + +/* Setup a new context using the given stack. + */ +struct context * +init_context(uint8_t *stack, uint32_t stack_size, int num_params) +{ + struct context *ctx; + + ctx = (struct context *) + (stack + stack_size - (sizeof(*ctx) + num_params*sizeof(uint32_t))); + memset(ctx, 0, sizeof(*ctx)); + + /* Fill in reasonable default for flat memory model */ + ctx->gdt_base = virt_to_phys(gdt); + ctx->gdt_limit = GDT_LIMIT; + ctx->cs = FLAT_CS; + ctx->ds = FLAT_DS; + ctx->es = FLAT_DS; + ctx->fs = FLAT_DS; + ctx->gs = FLAT_DS; + ctx->ss = FLAT_DS; + ctx->esp = virt_to_phys(ESP_LOC(ctx)); + ctx->return_addr = virt_to_phys(__exit_context); + + return ctx; +} + +/* Switch to another context. */ +struct context *switch_to(struct context *ctx) +{ + struct context *save, *ret; + + save = __context; + __context = ctx; + asm ("pushl %cs; call __switch_context"); + ret = __context; + __context = save; + return ret; +} + +#if 0 +//We will use elf_start in Etherboot +/* Start ELF Boot image */ +uint32_t start_elf(uint32_t entry_point, uint32_t param) +{ + struct context *ctx; + + ctx = init_context(image_stack, sizeof image_stack, 1); + ctx->eip = entry_point; + ctx->param[0] = param; + ctx->eax = 0xe1fb007; + ctx->ebx = param; + + ctx = switch_to(ctx); + return ctx->eax; +} +#endif diff --git a/src/filo/i386/context.h b/src/filo/i386/context.h new file mode 100644 index 00000000..b8731795 --- /dev/null +++ b/src/filo/i386/context.h @@ -0,0 +1,50 @@ +#ifndef i386_CONTEXT_H +#define i386_CONTEXT_H + +#include + +struct context { + /* Stack Segment, placed here because of the alignment issue... */ + uint16_t ss; + /* Used with sgdt/lgdt */ + uint16_t gdt_limit; + uint32_t gdt_base; + /* General registers, accessed with pushal/popal */ + uint32_t edi; + uint32_t esi; + uint32_t ebp; + uint32_t esp; /* points just below eax */ + uint32_t ebx; + uint32_t edx; + uint32_t ecx; + uint32_t eax; +#define ESP_LOC(ctx) (&(ctx)->gs) + /* Segment registers */ + uint32_t gs; + uint32_t fs; + uint32_t es; + uint32_t ds; + /* Flags */ + uint32_t eflags; + /* Code segment:offset */ + uint32_t eip; + uint32_t cs; + /* Optional stack contents */ + uint32_t return_addr; + uint32_t param[0]; +}; + +/* Create a new context in the given stack */ +struct context * +init_context(uint8_t *stack, uint32_t stack_size, int num_param); + +/* Switch context */ +struct context *switch_to(struct context *); + +/* Holds physical address of boot context */ +extern unsigned long __boot_ctx; + +/* This can always be safely used to refer to the boot context */ +#define boot_ctx ((struct context *) phys_to_virt(__boot_ctx)) + +#endif /* i386_CONTEXT_H */ diff --git a/src/filo/i386/linux_load.c b/src/filo/i386/linux_load.c new file mode 100644 index 00000000..42e3edfc --- /dev/null +++ b/src/filo/i386/linux_load.c @@ -0,0 +1,629 @@ +/* + * Linux/i386 loader + * Supports bzImage, zImage and Image format. + * + * Based on work by Steve Gehlbach. + * Portions are taken from mkelfImage. + * + * 2003-09 by SONE Takeshi + */ +#include + +#include + +#include +#include + +#include "context.h" +#include "segment.h" + +#define DEBUG_THIS DEBUG_LINUXLOAD +#include + +#define LINUX_PARAM_LOC 0x90000 +#define COMMAND_LINE_LOC 0x91000 +#define GDT_LOC 0x92000 +#define STACK_LOC 0x93000 + +/* The header of Linux/i386 kernel */ +struct linux_header { + uint8_t reserved1[0x1f1]; /* 0x000 */ + uint8_t setup_sects; /* 0x1f1 */ + uint16_t root_flags; /* 0x1f2 */ + uint8_t reserved2[6]; /* 0x1f4 */ + uint16_t vid_mode; /* 0x1fa */ + uint16_t root_dev; /* 0x1fc */ + uint16_t boot_sector_magic; /* 0x1fe */ + /* 2.00+ */ + uint8_t reserved3[2]; /* 0x200 */ + uint8_t header_magic[4]; /* 0x202 */ + uint16_t protocol_version; /* 0x206 */ + uint32_t realmode_swtch; /* 0x208 */ + uint16_t start_sys; /* 0x20c */ + uint16_t kver_addr; /* 0x20e */ + uint8_t type_of_loader; /* 0x210 */ + uint8_t loadflags; /* 0x211 */ + uint16_t setup_move_size; /* 0x212 */ + uint32_t code32_start; /* 0x214 */ + uint32_t ramdisk_image; /* 0x218 */ + uint32_t ramdisk_size; /* 0x21c */ + uint8_t reserved4[4]; /* 0x220 */ + /* 2.01+ */ + uint16_t heap_end_ptr; /* 0x224 */ + uint8_t reserved5[2]; /* 0x226 */ + /* 2.02+ */ + uint32_t cmd_line_ptr; /* 0x228 */ + /* 2.03+ */ + uint32_t initrd_addr_max; /* 0x22c */ +} __attribute__ ((packed)); + + +/* Paramters passed to 32-bit part of Linux + * This is another view of the structure above.. */ +struct linux_params { + uint8_t orig_x; /* 0x00 */ + uint8_t orig_y; /* 0x01 */ + uint16_t ext_mem_k; /* 0x02 -- EXT_MEM_K sits here */ + uint16_t orig_video_page; /* 0x04 */ + uint8_t orig_video_mode; /* 0x06 */ + uint8_t orig_video_cols; /* 0x07 */ + uint16_t unused2; /* 0x08 */ + uint16_t orig_video_ega_bx; /* 0x0a */ + uint16_t unused3; /* 0x0c */ + uint8_t orig_video_lines; /* 0x0e */ + uint8_t orig_video_isVGA; /* 0x0f */ + uint16_t orig_video_points; /* 0x10 */ + + /* VESA graphic mode -- linear frame buffer */ + uint16_t lfb_width; /* 0x12 */ + uint16_t lfb_height; /* 0x14 */ + uint16_t lfb_depth; /* 0x16 */ + uint32_t lfb_base; /* 0x18 */ + uint32_t lfb_size; /* 0x1c */ + uint16_t cl_magic; /* 0x20 */ +#define CL_MAGIC_VALUE 0xA33F + uint16_t cl_offset; /* 0x22 */ + uint16_t lfb_linelength; /* 0x24 */ + uint8_t red_size; /* 0x26 */ + uint8_t red_pos; /* 0x27 */ + uint8_t green_size; /* 0x28 */ + uint8_t green_pos; /* 0x29 */ + uint8_t blue_size; /* 0x2a */ + uint8_t blue_pos; /* 0x2b */ + uint8_t rsvd_size; /* 0x2c */ + uint8_t rsvd_pos; /* 0x2d */ + uint16_t vesapm_seg; /* 0x2e */ + uint16_t vesapm_off; /* 0x30 */ + uint16_t pages; /* 0x32 */ + uint8_t reserved4[12]; /* 0x34 -- 0x3f reserved for future expansion */ + + //struct apm_bios_info apm_bios_info; /* 0x40 */ + uint8_t apm_bios_info[0x40]; + //struct drive_info_struct drive_info; /* 0x80 */ + uint8_t drive_info[0x20]; + //struct sys_desc_table sys_desc_table; /* 0xa0 */ + uint8_t sys_desc_table[0x140]; + uint32_t alt_mem_k; /* 0x1e0 */ + uint8_t reserved5[4]; /* 0x1e4 */ + uint8_t e820_map_nr; /* 0x1e8 */ + uint8_t reserved6[9]; /* 0x1e9 */ + uint16_t mount_root_rdonly; /* 0x1f2 */ + uint8_t reserved7[4]; /* 0x1f4 */ + uint16_t ramdisk_flags; /* 0x1f8 */ +#define RAMDISK_IMAGE_START_MASK 0x07FF +#define RAMDISK_PROMPT_FLAG 0x8000 +#define RAMDISK_LOAD_FLAG 0x4000 + uint8_t reserved8[2]; /* 0x1fa */ + uint16_t orig_root_dev; /* 0x1fc */ + uint8_t reserved9[1]; /* 0x1fe */ + uint8_t aux_device_info; /* 0x1ff */ + uint8_t reserved10[2]; /* 0x200 */ + uint8_t param_block_signature[4]; /* 0x202 */ + uint16_t param_block_version; /* 0x206 */ + uint8_t reserved11[8]; /* 0x208 */ + uint8_t loader_type; /* 0x210 */ +#define LOADER_TYPE_LOADLIN 1 +#define LOADER_TYPE_BOOTSECT_LOADER 2 +#define LOADER_TYPE_SYSLINUX 3 +#define LOADER_TYPE_ETHERBOOT 4 +#define LOADER_TYPE_KERNEL 5 + uint8_t loader_flags; /* 0x211 */ + uint8_t reserved12[2]; /* 0x212 */ + uint32_t kernel_start; /* 0x214 */ + uint32_t initrd_start; /* 0x218 */ + uint32_t initrd_size; /* 0x21c */ + uint8_t reserved12_5[8]; /* 0x220 */ + uint32_t cmd_line_ptr; /* 0x228 */ + uint8_t reserved13[164]; /* 0x22c */ + struct e820entry e820_map[E820MAX]; /* 0x2d0 */ + uint8_t reserved16[688]; /* 0x550 */ +#define COMMAND_LINE_SIZE 256 + /* Command line is copied here by 32-bit i386/kernel/head.S. + * So I will follow the boot protocol, rather than putting it + * directly here. --ts1 */ + uint8_t command_line[COMMAND_LINE_SIZE]; /* 0x800 */ + uint8_t reserved17[1792]; /* 0x900 - 0x1000 */ +}; + +uint64_t forced_memsize; + +/* Load the first part the file and check if it's Linux */ +static uint32_t load_linux_header(struct linux_header *hdr) +{ + int load_high; + uint32_t kern_addr; + + if (file_read(hdr, sizeof *hdr) != sizeof *hdr) { + debug("Can't read Linux header\n"); + return 0; + } + if (hdr->boot_sector_magic != 0xaa55) { + debug("Not a Linux kernel image\n"); + return 0; + } + + /* Linux is found. Print some information */ + if (memcmp(hdr->header_magic, "HdrS", 4) != 0) { + /* This may be floppy disk image or something. + * Perform a simple (incomplete) sanity check. */ + if (hdr->setup_sects >= 16 + || file_size() - (hdr->setup_sects<<9) >= 512<<10) { + debug("This looks like a bootdisk image but not like Linux...\n"); + return 0; + } + + debugx("Possible very old Linux"); + /* This kernel does not even have a protocol version. + * Force the value. */ + hdr->protocol_version = 0; /* pre-2.00 */ + } else + printf("Found Linux"); + if (hdr->protocol_version >= 0x200 && hdr->kver_addr) { + char kver[256]; + file_seek(hdr->kver_addr + 0x200); + if (file_read(kver, sizeof kver) != 0) { + kver[255] = 0; + printf(" version %s", kver); + } + } + debug(" (protocol %#x)", hdr->protocol_version); + load_high = 0; + if (hdr->protocol_version >= 0x200) { + debug(" (loadflags %#x)", hdr->loadflags); + load_high = hdr->loadflags & 1; + } + if (load_high) { + printf(" bzImage"); + kern_addr = 0x100000; + } else { + printf(" zImage or Image"); + kern_addr = 0x1000; + } + printf(".\n"); + + return kern_addr; +} + +/* Set up parameters for 32-bit kernel */ +static void +init_linux_params(struct linux_params *params, struct linux_header *hdr) +{ + debug("Setting up paramters at %#lx\n", virt_to_phys(params)); + memset(params, 0, sizeof *params); + + /* Copy some useful values from header */ + params->mount_root_rdonly = hdr->root_flags; + params->orig_root_dev = hdr->root_dev; + + /* Video parameters. + * This assumes we have VGA in standard 80x25 text mode, + * just like our vga.c does. + * Cursor position is filled later to allow some more printf's. */ + params->orig_video_mode = 3; + params->orig_video_cols = 80; + params->orig_video_lines = 25; + params->orig_video_isVGA = 1; + params->orig_video_points = 16; + + params->loader_type = 0xff; /* Unregistered Linux loader */ +} + +/* Memory map */ +static void +set_memory_size(struct linux_params *params, struct sys_info *info) +{ + uint32_t i; + uint32_t ramtop = 0; + struct e820entry *linux_map; + struct e820entry *filo_map; + + linux_map = params->e820_map; + + filo_map = meminfo.map; + for (i = 0; i < meminfo.map_count; i++, linux_map++, filo_map++) { + if (i < E820MAX) { + /* Convert to BIOS e820 style */ + linux_map->addr = filo_map->addr; + linux_map->size = filo_map->size; + linux_map->type = filo_map->type; +// debug("%016Lx - %016Lx\n", linux_map->addr,linux_map->addr + linux_map->size); + params->e820_map_nr = i+1; + } + + } + ramtop = meminfo.memsize; + debug("ramtop=%#xk\n", ramtop); + /* Size of memory above 1MB in KB */ + params->alt_mem_k = ramtop; + /* old style, 64MB max */ + if (ramtop >= (64<<10)) + params->ext_mem_k = (63<<10); + else + params->ext_mem_k = params->alt_mem_k; + + debug("ext_mem_k=%d, alt_mem_k=%d\n", params->ext_mem_k, params->alt_mem_k); +} + +/* + * Parse command line + * Some parameters, like initrd=, are not passed to kernel, + * we are responsible to process them. + * Parameters for kernel are copied to kern_cmdline. Returns name of initrd. + */ +static char *parse_command_line(const char *orig_cmdline, char *kern_cmdline) +{ + const char *start, *sep, *end, *val; + char name[64]; + int len; + int k_len; + int to_kern; + char *initrd = 0; + int toolong = 0; + + forced_memsize = 0; + + if (!orig_cmdline) { + *kern_cmdline = 0; + return 0; + } + + k_len = 0; + debug("original command line: \"%s\"\n", orig_cmdline); + debug("kernel command line at %#lx\n", virt_to_phys(kern_cmdline)); + + start = orig_cmdline; + while (*start == ' ') + start++; + while (*start) { + end = strchr(start, ' '); + if (!end) + end = start + strlen(start); + sep = strchr(start, '='); + if (!sep || sep > end) + sep = end; + len = sep - start; + if (len >= sizeof(name)) + len = sizeof(name) - 1; + memcpy(name, start, len); + name[len] = 0; + + if (*sep == '=') { + val = sep + 1; + len = end - val; + } else { + val = 0; + len = 0; + } + + /* Only initrd= and mem= are handled here. vga= is not, + * which I believe is a paramter to the realmode part of Linux, + * which we don't execute. */ + if (strcmp(name, "initrd") == 0) { + if (!val) + printf("Missing filename to initrd parameter\n"); + else { + initrd = allot(len + 1); + memcpy(initrd, val, len); + initrd[len] = 0; + debug("initrd=%s\n", initrd); + } + /* Don't pass this to kernel */ + to_kern = 0; + } else if (strcmp(name, "mem") == 0) { + if (!val) + printf("Missing value for mem parameter\n"); + else { + forced_memsize = strtoull_with_suffix(val, (char**)&val, 0); + if (forced_memsize == 0) + printf("Invalid mem option, ignored\n"); + if (val != end) { + printf("Garbage after mem=, ignored\n"); + forced_memsize = 0; + } +// debug("mem=%Lu\n", forced_memsize); + } + /* mem= is for both loader and kernel */ + to_kern = 1; + } else + to_kern = 1; + + if (to_kern) { + /* Copy to kernel command line buffer */ + if (k_len != 0) + kern_cmdline[k_len++] = ' '; /* put separator */ + len = end - start; + if (k_len + len >= COMMAND_LINE_SIZE) { + len = COMMAND_LINE_SIZE - k_len - 1; + if (!toolong) { + printf("Kernel command line is too long; truncated to " + "%d bytes\n", COMMAND_LINE_SIZE-1); + toolong = 1; + } + } + memcpy(kern_cmdline + k_len, start, len); + k_len += len; + } + + start = end; + while (*start == ' ') + start++; + } + kern_cmdline[k_len] = 0; + debug("kernel command line (%d bytes): \"%s\"\n", k_len, kern_cmdline); + + return initrd; +} + +/* Set command line location */ +static void set_command_line_loc(struct linux_params *params, + struct linux_header *hdr) +{ + if (hdr->protocol_version >= 0x202) { + /* new style */ + params->cmd_line_ptr = COMMAND_LINE_LOC; + } else { + /* old style */ + params->cl_magic = CL_MAGIC_VALUE; + params->cl_offset = COMMAND_LINE_LOC - LINUX_PARAM_LOC; + } +} + +/* Load 32-bit part of kernel */ +static int load_linux_kernel(struct linux_header *hdr, uint32_t kern_addr) +{ + uint32_t kern_offset, kern_size; + + if (hdr->setup_sects == 0) + hdr->setup_sects = 4; + kern_offset = (hdr->setup_sects + 1) * 512; + file_seek(kern_offset); + kern_size = file_size() - kern_offset; + debug("offset=%#x addr=%#x size=%#x\n", kern_offset, kern_addr, kern_size); + + if (using_devsize) { + printf("Attempt to load up to end of device as kernel; " + "specify the image size\n"); + return 0; + } + + printf("Loading kernel... "); + if (file_read(phys_to_virt(kern_addr), kern_size) != kern_size) { + printf("Can't read kernel\n"); + return 0; + } + printf("ok\n"); + + return kern_size; +} + +static int load_initrd(struct linux_header *hdr, struct sys_info *info, + uint32_t kern_end, struct linux_params *params, const char *initrd_file) +{ + uint32_t max; + uint32_t start, end, size; + uint64_t forced; + extern char _virt_start[], _end[]; + + if (!file_open(initrd_file)) { + printf("Can't open initrd: %s\n", initrd_file); + return -1; + } + if (using_devsize) { + printf("Attempt to load up to end of device as initrd; " + "specify the image size\n"); + return -1; + } + size = file_size(); + + + /* Find out the kernel's restriction on how high the initrd can be + * placed */ + if (hdr->protocol_version >= 0x203) + max = hdr->initrd_addr_max; + else + max = 0x38000000; /* Hardcoded value for older kernels */ + + /* FILO itself is at the top of RAM. (relocated) + * So, try putting initrd just below us. */ + end = virt_to_phys(_virt_start); + if (end > max) + end = max; + + /* If "mem=" option is given, we have to put the initrd within + * the specified range. */ + if (forced_memsize) { + forced = forced_memsize; + if (forced > max) + forced = max; + /* If the "mem=" is lower, it's easy */ + if (forced <= end) + end = forced; + else { + /* Otherwise, see if we can put it above us */ + if (virt_to_phys(_end) + size <= forced) + end = forced; /* Ok */ + } + } + + start = end - size; + start &= ~0xfff; /* page align */ + end = start + size; + + debug("start=%#x end=%#x\n", start, end); + + if (start < kern_end) { + printf("Initrd is too big\n"); + return -1; + } + + printf("Loading initrd... "); + if (file_read(phys_to_virt(start), size) != size) { + printf("Can't read initrd\n"); + return -1; + } + printf("ok\n"); + + params->initrd_start = start; + params->initrd_size = size; + + return 0; +} + +static void hardware_setup(void) +{ + /* Disable nmi */ + outb(0x80, 0x70); + + /* Make sure any coprocessor is properly reset.. */ + outb(0, 0xf0); + outb(0, 0xf1); + + /* we're getting screwed again and again by this problem of the 8259. + * so we're going to leave this lying around for inclusion into + * crt0.S on an as-needed basis. + * + * well, that went ok, I hope. Now we have to reprogram the interrupts :-( + * we put them right after the intel-reserved hardware interrupts, at + * int 0x20-0x2F. There they won't mess up anything. Sadly IBM really + * messed this up with the original PC, and they haven't been able to + * rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f, + * which is used for the internal hardware interrupts as well. We just + * have to reprogram the 8259's, and it isn't fun. + */ + + outb(0x11, 0x20); /* initialization sequence to 8259A-1 */ + outb(0x11, 0xA0); /* and to 8259A-2 */ + + outb(0x20, 0x21); /* start of hardware int's (0x20) */ + outb(0x28, 0xA1); /* start of hardware int's 2 (0x28) */ + + outb(0x04, 0x21); /* 8259-1 is master */ + outb(0x02, 0xA1); /* 8259-2 is slave */ + + outb(0x01, 0x21); /* 8086 mode for both */ + outb(0x01, 0xA1); + + outb(0xFF, 0xA1); /* mask off all interrupts for now */ + outb(0xFB, 0x21); /* mask all irq's but irq2 which is cascaded */ +} + +/* Start Linux */ +static int start_linux(uint32_t kern_addr, struct linux_params *params) +{ + struct segment_desc *linux_gdt; + struct context *ctx; +#if 0 + extern int cursor_x, cursor_y; +#endif + + ctx = init_context(phys_to_virt(STACK_LOC), 4096, 0); + + /* Linux expects GDT being in low memory */ + linux_gdt = phys_to_virt(GDT_LOC); + memset(linux_gdt, 0, 13*sizeof(struct segment_desc)); + /* Normal kernel code/data segments */ + linux_gdt[2] = gdt[FLAT_CODE]; + linux_gdt[3] = gdt[FLAT_DATA]; + /* 2.6 kernel uses 12 and 13, but head.S uses backward-compatible + * segments (2 and 3), so it SHOULD not be a problem. + * However, some distro kernels (eg. RH9) with backported threading + * patch use 12 and 13 also when booting... */ + linux_gdt[12] = gdt[FLAT_CODE]; + linux_gdt[13] = gdt[FLAT_DATA]; + ctx->gdt_base = GDT_LOC; + ctx->gdt_limit = 14*8-1; + ctx->cs = 0x10; + ctx->ds = 0x18; + ctx->es = 0x18; + ctx->fs = 0x18; + ctx->gs = 0x18; + ctx->ss = 0x18; + + /* Parameter location */ + ctx->esi = virt_to_phys(params); + + /* Entry point */ + ctx->eip = kern_addr; + + debug("eip=%#x\n", kern_addr); + printf("Jumping to entry point...\n"); + +#ifdef VGA_CONSOLE + /* Update VGA cursor position. + * This must be here because the printf changes the value! */ +#if 0 + params->orig_x = cursor_x; + params->orig_y = cursor_y; +#endif +#endif + + /* Go... */ + ctx = switch_to(ctx); + + /* It's impossible but... */ + printf("Returned with eax=%#x\n", ctx->eax); + + return ctx->eax; +} + +int linux_load(struct sys_info *info, const char *file, const char *cmdline) +{ + struct linux_header hdr; + struct linux_params *params; + uint32_t kern_addr, kern_size; + char *initrd_file = 0; + + if (!file_open(file)) + return -1; + + kern_addr = load_linux_header(&hdr); + if (kern_addr == 0) + return LOADER_NOT_SUPPORT; + + params = phys_to_virt(LINUX_PARAM_LOC); + init_linux_params(params, &hdr); + set_memory_size(params, info); + initrd_file = parse_command_line(cmdline, phys_to_virt(COMMAND_LINE_LOC)); + set_command_line_loc(params, &hdr); + + kern_size = load_linux_kernel(&hdr, kern_addr); + if (kern_size == 0) { + if (initrd_file) + forget(initrd_file); + return -1; + } + + if (initrd_file) { + if (load_initrd(&hdr, info, kern_addr+kern_size, params, initrd_file) + != 0) { + forget(initrd_file); + return -1; + } + forget(initrd_file); + } + + hardware_setup(); + + start_linux(kern_addr, params); + return 0; +} diff --git a/src/filo/i386/multiboot.c b/src/filo/i386/multiboot.c new file mode 100644 index 00000000..e4a49569 --- /dev/null +++ b/src/filo/i386/multiboot.c @@ -0,0 +1,129 @@ +/* Support for Multiboot */ +#include + +#include +#include +#include + +#define DEBUG_THIS DEBUG_MULTIBOOT +#include + +/* Multiboot header, gives information to loader */ + +#define MULTIBOOT_HEADER_MAGIC 0x1BADB002 +#define MULTIBOOT_HEADER_FLAGS 0x00000003 + +struct mbheader { + unsigned int magic, flags, checksum; +}; +const struct mbheader multiboot_header + __attribute__((section (".hdr"))) = +{ + MULTIBOOT_HEADER_MAGIC, + MULTIBOOT_HEADER_FLAGS, + -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS) +}; + +/* Multiboot information structure, provided by loader to us */ + +struct multiboot_mmap { + unsigned entry_size; + unsigned base_lo, base_hi; + unsigned size_lo, size_hi; + unsigned type; +}; + +struct multiboot_info { + unsigned flags; +#define MULTIBOOT_MEM_VALID 0x01 +#define MULTIBOOT_BOOT_DEV_VALID 0x02 +#define MULTIBOOT_CMDLINE_VALID 0x04 +#define MULTIBOOT_MODS_VALID 0x08 +#define MULTIBOOT_AOUT_SYMS_VALID 0x10 +#define MULTIBOOT_ELF_SYMS_VALID 0x20 +#define MULTIBOOT_MMAP_VALID 0x40 + unsigned mem_lower; + unsigned mem_upper; + unsigned char boot_device[4]; + unsigned command_line; + unsigned mods_count; + unsigned mods_addr; + unsigned syms_num; + unsigned syms_size; + unsigned syms_addr; + unsigned syms_shndx; + unsigned mmap_length; + unsigned mmap_addr; +}; + +void collect_multiboot_info(struct sys_info *info) +{ + struct multiboot_info *mbinfo; + struct multiboot_mmap *mbmem; + unsigned mbcount, mbaddr; + int i; + struct memrange *mmap; + int mmap_count; + + if (info->boot_type != 0x2BADB002) + return; + + debug("Using Multiboot information at %#lx\n", info->boot_data); + + mbinfo = phys_to_virt(info->boot_data); + + if (mbinfo->flags & MULTIBOOT_MMAP_VALID) { + /* convert mmap records */ + mbmem = phys_to_virt(mbinfo->mmap_addr); + mbcount = mbinfo->mmap_length / (mbmem->entry_size + 4); + mmap = malloc(mbcount * sizeof *mmap); + mmap_count = 0; + mbaddr = mbinfo->mmap_addr; + for (i = 0; i < mbcount; i++) { + mbmem = phys_to_virt(mbaddr); + debug("%08x%08x %08x%08x (%d)\n", + mbmem->base_hi, + mbmem->base_lo, + mbmem->size_hi, + mbmem->size_lo, + mbmem->type); + if (mbmem->type == 1) { /* Only normal RAM */ + mmap[mmap_count].base = mbmem->base_lo + + (((unsigned long long) mbmem->base_hi) << 32); + mmap[mmap_count].size = mbmem->size_lo + + (((unsigned long long) mbmem->size_hi) << 32); + mmap_count++; + } + mbaddr += mbmem->entry_size + 4; + if (mbaddr >= mbinfo->mmap_addr + mbinfo->mmap_length) + break; + } + /* simple sanity check - there should be at least 2 RAM segments + * (base 640k and extended) */ + if (mmap_count >= 2) + goto got_it; + + printf("Multiboot mmap is broken\n"); + free(mmap); + /* fall back to mem_lower/mem_upper */ + } + + if (mbinfo->flags & MULTIBOOT_MEM_VALID) { + /* use mem_lower and mem_upper */ + mmap_count = 2; + mmap = malloc(2 * sizeof(*mmap)); + mmap[0].base = 0; + mmap[0].size = mbinfo->mem_lower << 10; + mmap[1].base = 1 << 20; /* 1MB */ + mmap[1].size = mbinfo->mem_upper << 10; + goto got_it; + } + + printf("Can't get memory information from Multiboot\n"); + return; + +got_it: + info->memrange = mmap; + info->n_memranges = mmap_count; + return; +} diff --git a/src/filo/i386/segment.c b/src/filo/i386/segment.c new file mode 100644 index 00000000..6e370d8b --- /dev/null +++ b/src/filo/i386/segment.c @@ -0,0 +1,48 @@ +/* Segmentation of the i386 architecture. + * + * 2003-07 by SONE Takeshi + */ +#include + +//#include +#include +#include "segment.h" + +#define DEBUG_THIS DEBUG_SEGMENT +#include + +/* i386 lgdt argument */ +struct gdtarg { + unsigned short limit; + unsigned int base; +} __attribute__((packed)); + +/* GDT, the global descriptor table */ +struct segment_desc gdt[NUM_SEG] = { + /* 0x00: null segment */ + {0, 0, 0, 0, 0, 0}, + /* 0x08: flat code segment */ + {0xffff, 0, 0, 0x9f, 0xcf, 0}, + /* 0x10: flat data segment */ + {0xffff, 0, 0, 0x93, 0xcf, 0}, + /* 0x18: code segment for relocated execution */ + {0xffff, 0, 0, 0x9f, 0xcf, 0}, + /* 0x20: data segment for relocated execution */ + {0xffff, 0, 0, 0x93, 0xcf, 0}, +}; + +/* Copy GDT to new location and reload it */ +void move_gdt(unsigned long newgdt) +{ + struct gdtarg gdtarg; + + debug("Moving GDT to %#lx...", newgdt); + memcpy(phys_to_virt(newgdt), gdt, sizeof gdt); + gdtarg.base = newgdt; + gdtarg.limit = GDT_LIMIT; + debug("reloading GDT..."); + __asm__ __volatile__ ("lgdt %0\n\t" : : "m" (gdtarg)); + debug("reloading CS for fun..."); + __asm__ __volatile__ ("ljmp %0, $1f\n1:" : : "n" (RELOC_CS)); + debug("ok\n"); +} diff --git a/src/filo/i386/segment.h b/src/filo/i386/segment.h new file mode 100644 index 00000000..97eb574e --- /dev/null +++ b/src/filo/i386/segment.h @@ -0,0 +1,29 @@ +/* Segment indexes. Must match the gdt definition in segment.c. */ +enum { + NULL_SEG, + FLAT_CODE, + FLAT_DATA, + RELOC_CODE, + RELOC_DATA, + NUM_SEG, +}; + +/* Values for segment selector register */ +#define FLAT_CS (FLAT_CODE << 3) +#define FLAT_DS (FLAT_DATA << 3) +#define RELOC_CS (RELOC_CODE << 3) +#define RELOC_DS (RELOC_DATA << 3) + +/* i386 segment descriptor */ +struct segment_desc { + unsigned short limit_0; + unsigned short base_0; + unsigned char base_16; + unsigned char types; + unsigned char flags; + unsigned char base_24; +}; + +extern struct segment_desc gdt[NUM_SEG]; + +#define GDT_LIMIT ((NUM_SEG << 3) - 1) diff --git a/src/filo/i386/switch.S b/src/filo/i386/switch.S new file mode 100644 index 00000000..76ac75bf --- /dev/null +++ b/src/filo/i386/switch.S @@ -0,0 +1,116 @@ + .globl entry, __switch_context, __exit_context, halt + + .text + .align 4 + +/* + * Entry point + * We start execution from here. + * It is assumed that CPU is in 32-bit protected mode and + * all segments are 4GB and base zero (flat model). + */ +entry: + /* Save boot context and switch to our main context. + * Main context is statically defined in C. + */ + pushl %cs + call __switch_context + + /* We get here when the main context switches back to + * the boot context. + * Return to previous bootloader. + */ + ret + +/* + * Switch execution context + * This saves registers, segments, and GDT in the stack, then + * switches the stack, and restores everything from the new stack. + * This function takes no argument. New stack pointer is + * taken from global variable __context, and old stack pointer + * is also saved to __context. This way we can just jump to + * this routine to get back to the original context. + * + * Call this routine with lcall or pushl %cs; call. + */ +__switch_context: + /* Save everything in current stack */ + pushfl /* 56 */ + pushl %ds /* 52 */ + pushl %es /* 48 */ + pushl %fs /* 44 */ + pushl %gs /* 40 */ + pushal /* 8 */ + subl $8, %esp + movw %ss, (%esp) /* 0 */ + sgdt 2(%esp) /* 2 */ + +#if 0 + /* Swap %cs and %eip on the stack, so lret will work */ + movl 60(%esp), %eax + xchgl %eax, 64(%esp) + movl %eax, 60(%esp) +#endif + + /* At this point we don't know if we are on flat segment + * or relocated. So compute the address offset from %eip. + * Assuming CS.base==DS.base==SS.base. + */ + call 1f +1: popl %ebx + subl $1b, %ebx + + /* Interrupts are not allowed... */ + cli + + /* Current context pointer is our stack pointer */ + movl %esp, %esi + + /* Normalize the ctx pointer */ + subl %ebx, %esi + + /* Swap it with new value */ + xchgl %esi, __context(%ebx) + + /* Adjust new ctx pointer for current address offset */ + addl %ebx, %esi + + /* Load new %ss and %esp to temporary */ + movzwl (%esi), %edx + movl 20(%esi), %eax + + /* Load new GDT */ + lgdt 2(%esi) + + /* Load new stack segment with new GDT */ + movl %edx, %ss + + /* Set new stack pointer, but we have to adjust it because + * pushal saves %esp value before pushal, and we want the value + * after pushal. + */ + leal -32(%eax), %esp + + /* Load the rest from new stack */ + popal + popl %gs + popl %fs + popl %es + popl %ds + popfl + + /* Finally, load new %cs and %eip */ + lret + +__exit_context: + /* Get back to the original context */ + pushl %cs + call __switch_context + + /* We get here if the other context attempt to switch to this + * dead context. This should not happen. */ + +halt: + cli + hlt + jmp halt diff --git a/src/filo/i386/sys_info.c b/src/filo/i386/sys_info.c new file mode 100644 index 00000000..fa8c190f --- /dev/null +++ b/src/filo/i386/sys_info.c @@ -0,0 +1,29 @@ +#include +#include +#include "context.h" +#define DEBUG_THIS DEBUG_SYS_INFO +#include + +void collect_multiboot_info(struct sys_info *); + +void collect_sys_info(struct sys_info *info) +{ + + /* Pick up paramters given by bootloader to us */ + info->boot_type = boot_ctx->eax; + info->boot_data = boot_ctx->ebx; + info->boot_arg = boot_ctx->param[0]; + debug("boot eax = %#lx\n", info->boot_type); + debug("boot ebx = %#lx\n", info->boot_data); + debug("boot arg = %#lx\n", info->boot_arg); + + collect_elfboot_info(info); + collect_linuxbios_info(info); +#ifdef MULTIBOOT_IMAGE + collect_multiboot_info(info); +#endif + +#if 0 + debug("RAM %Ld MB\n", (meminfo.memsize + 512*1024) >> 20); +#endif +} diff --git a/src/filo/main/console_x.c b/src/filo/main/console_x.c new file mode 100644 index 00000000..fd04a939 --- /dev/null +++ b/src/filo/main/console_x.c @@ -0,0 +1,74 @@ + +#include "etherboot.h" + +#include + +int getline(char *buf, int max) +{ + int cur, ch, nonspace_seen; + + cur = 0; + while (buf[cur]) { + putchar(buf[cur]); + cur++; + } + for (;;) { + ch = getchar(); + switch (ch) { + /* end of line */ + case '\r': + case '\n': + putchar('\n'); + goto out; + /* backspace */ + case '\b': + case '\x7f': + if (cur > 0) { + cur--; + putchar('\b'); + putchar(' '); + putchar('\b'); + } + break; + /* word erase */ + case 'W' & 0x1f: /* ^W */ + nonspace_seen = 0; + while (cur) { + if (buf[cur-1] != ' ') + nonspace_seen = 1; + putchar('\b'); + putchar(' '); + putchar('\b'); + cur--; + if (nonspace_seen && cur < max-1 && cur > 0 && buf[cur-1]==' ') + break; + } + break; + /* line erase */ + case 'U' & 0x1f: /* ^U */ + while (cur) { + putchar('\b'); + putchar(' '); + putchar('\b'); + cur--; + } + cur = 0; + break; + default: + if (ch < 0x20) + break; /* ignore control char */ + if (ch >= 0x7f) + break; + if (cur + 1 < max) { + putchar(ch); /* echo back */ + buf[cur] = ch; + cur++; + } + } + } +out: + if (cur >= max) + cur = max - 1; + buf[cur] = '\0'; + return cur; +} diff --git a/src/filo/main/elfload.c b/src/filo/main/elfload.c new file mode 100644 index 00000000..53114ffb --- /dev/null +++ b/src/filo/main/elfload.c @@ -0,0 +1,398 @@ +/* ELF Boot loader + * As we have seek, this implementation can be straightforward. + * 2003-07 by SONE Takeshi + */ +#include +#include +#include +#include +#include +#include + +#include +#define DEBUG_THIS DEBUG_ELFBOOT +#include + +#if 1 +//Use that in Etherboot +extern int elf_start(unsigned long __unused_i386, unsigned long entry, unsigned long param); +#define start_elf(x,y) elf_start(0, x, y) +#else +// original in filo +extern unsigned int start_elf(unsigned long entry_point, unsigned long param); +#endif + +extern char _virt_start[], _end[]; + +static char *image_name, *image_version; + +static int check_mem_ranges(struct sys_info *info, + Elf_phdr *phdr, int phnum) +{ + int i, j; + unsigned long start, end; + unsigned long prog_start, prog_end; +#if 0 + struct memrange *mem; +#else + struct e820entry *mem; +#endif + + prog_start = virt_to_phys(&_virt_start); + prog_end = virt_to_phys(&_end); + + for (i = 0; i < phnum; i++) { + if (phdr[i].p_type != PT_LOAD) + continue; + start = phdr[i].p_paddr; + end = start + phdr[i].p_memsz; + if (start < prog_start && end > prog_start) + goto conflict; + if (start < prog_end && end > prog_end) + goto conflict; +#if 0 + for (j = 0; j < info->n_memranges; j++) { + mem = &info->memrange[j]; + if (mem->base <= start && mem->base + mem->size >= end) + break; + } + if (j >= info->n_memranges) + goto badseg; +#else +#define LB_MEM_RAM 1 + for (j = 0; j < meminfo.map_count; j++) { + mem = &meminfo.map[j]; + if (mem->type!=LB_MEM_RAM) continue; + if (mem->addr <= start && mem->addr + mem->size >= end) + break; + } + if (j >= meminfo.map_count) + goto badseg; +#endif + } + return 1; + +conflict: + printf("%s occupies [%#lx-%#lx]\n", program_name, prog_start, prog_end); + +badseg: + printf("Segment %d [%#lx-%#lx] doesn't fit into memory\n", i, start, end-1); + return 0; +} + +static unsigned long process_image_notes(Elf_phdr *phdr, int phnum, + unsigned short *sum_ptr) +{ + int i; + char *buf = NULL; + int retval = 0; + unsigned long addr, end; + Elf_Nhdr *nhdr; + const char *name; + void *desc; + + for (i = 0; i < phnum; i++) { + if (phdr[i].p_type != PT_NOTE) + continue; + buf = allot(phdr[i].p_filesz); + file_seek(phdr[i].p_offset); + if (file_read(buf, phdr[i].p_filesz) != phdr[i].p_filesz) { + printf("Can't read note segment\n"); + goto out; + } + addr = (unsigned long) buf; + end = addr + phdr[i].p_filesz; + while (addr < end) { + nhdr = (Elf_Nhdr *) addr; + addr += sizeof(Elf_Nhdr); + name = (const char *) addr; + addr += (nhdr->n_namesz+3) & ~3; + desc = (void *) addr; + addr += (nhdr->n_descsz+3) & ~3; + + if (nhdr->n_namesz==sizeof(ELF_NOTE_BOOT) + && memcmp(name, ELF_NOTE_BOOT, sizeof(ELF_NOTE_BOOT))==0) { + if (nhdr->n_type == EIN_PROGRAM_NAME) { + image_name = calloc(1, nhdr->n_descsz + 1); + memcpy(image_name, desc, nhdr->n_descsz); + } + if (nhdr->n_type == EIN_PROGRAM_VERSION) { + image_version = calloc(1, nhdr->n_descsz + 1); + memcpy(image_version, desc, nhdr->n_descsz); + } + if (nhdr->n_type == EIN_PROGRAM_CHECKSUM) { + *sum_ptr = *(unsigned short *) desc; + debug("Image checksum: %04x\n", *sum_ptr); + /* Where in the file */ + retval = phdr[i].p_offset + + (unsigned long) desc - (unsigned long) buf; + } + } + } + } +out: + if (buf) + forget(buf); + return retval; +} + +static int load_segments(Elf_phdr *phdr, int phnum, + unsigned long checksum_offset) +{ + unsigned long bytes; + unsigned int start_time, time; + int i; + int j; + + bytes = 0; + start_time = currticks(); +#if 0 + for (j = 0; j < phnum; j++) { + if (phdr[j].p_type != PT_LOAD) + continue; + debug("0 segment %d addr:%#x file:%#x mem:%#x, phdr%#x\n", + j, phdr[j].p_paddr, phdr[j].p_filesz, phdr[j].p_memsz, virt_to_phys(&phdr[j])); + } +#endif + + for (i = 0; i < phnum; i++) { + if (phdr[i].p_type != PT_LOAD) + continue; + debug("segment %d addr:%#x file:%#x mem:%#x phdr:%#x ", + i, phdr[i].p_paddr, phdr[i].p_filesz, phdr[i].p_memsz, virt_to_phys(&phdr[i])); + file_seek(phdr[i].p_offset); + debug("loading... "); + if (file_read(phys_to_virt(phdr[i].p_paddr), phdr[i].p_filesz) + != phdr[i].p_filesz) { + printf("Can't read program segment %d\n", i); + return 0; + } + bytes += phdr[i].p_filesz; + debug("clearing... "); + memset(phys_to_virt(phdr[i].p_paddr + phdr[i].p_filesz), 0, + phdr[i].p_memsz - phdr[i].p_filesz); + if (phdr[i].p_offset <= checksum_offset + && phdr[i].p_offset + phdr[i].p_filesz >= checksum_offset+2) { + debug("clearing checksum... "); + memset(phys_to_virt(phdr[i].p_paddr + checksum_offset + - phdr[i].p_offset), 0, 2); + } + debug("ok\n"); + + } + time = (currticks() - start_time)*1000/18; + printf("Loaded %d bytes in %dms (%dKB/s)\n", bytes, time, + time? bytes/time : 0); + return 1; +} + +static int verify_image(Elf_ehdr *ehdr, Elf_phdr *phdr, int phnum, + unsigned short image_sum) +{ + unsigned short sum, part_sum; + unsigned long offset; + int i; + + sum = 0; + offset = 0; + + part_sum = ipchksum(ehdr, sizeof *ehdr); + sum = add_ipchksums(offset, sum, part_sum); + offset += sizeof *ehdr; + + part_sum = ipchksum(phdr, phnum * sizeof(*phdr)); + sum = add_ipchksums(offset, sum, part_sum); + offset += phnum * sizeof(*phdr); + + for (i = 0; i < phnum; i++) { + if (phdr[i].p_type != PT_LOAD) + continue; + part_sum = ipchksum(phys_to_virt(phdr[i].p_paddr), phdr[i].p_memsz); + sum = add_ipchksums(offset, sum, part_sum); + offset += phdr[i].p_memsz; + } + + if (sum != image_sum) { + printf("Verify FAILED (image:%04x vs computed:%04x)\n", + image_sum, sum); + return 0; + } + return 1; +} + +static inline unsigned const padded(unsigned s) +{ + return (s + 3) & ~3; +} + +static Elf_Bhdr *add_boot_note(Elf_Bhdr *bhdr, const char *name, + unsigned type, const char *desc, unsigned descsz) +{ + Elf_Nhdr nhdr; + unsigned ent_size, new_size, pad; + char *addr; + + if (!bhdr) + return NULL; + + nhdr.n_namesz = name? strlen(name)+1 : 0; + nhdr.n_descsz = descsz; + nhdr.n_type = type; + ent_size = sizeof(nhdr) + padded(nhdr.n_namesz) + padded(nhdr.n_descsz); + if (bhdr->b_size + ent_size > 0xffff) { + printf("Boot notes too big\n"); + forget(bhdr); + return NULL; + } + if (bhdr->b_size + ent_size > bhdr->b_checksum) { + do { + new_size = bhdr->b_checksum * 2; + } while (new_size < bhdr->b_size + ent_size); + if (new_size > 0xffff) + new_size = 0xffff; + debug("expanding boot note size to %u\n", new_size); + bhdr = realloc(bhdr, new_size); + bhdr->b_checksum = new_size; + } + + addr = (char *) bhdr; + addr += bhdr->b_size; + memcpy(addr, &nhdr, sizeof(nhdr)); + addr += sizeof(nhdr); + + memcpy(addr, name, nhdr.n_namesz); + addr += nhdr.n_namesz; + pad = padded(nhdr.n_namesz) - nhdr.n_namesz; + memset(addr, 0, pad); + addr += pad; + + memcpy(addr, desc, nhdr.n_descsz); + addr += nhdr.n_descsz; + pad = padded(nhdr.n_descsz) - nhdr.n_descsz; + memset(addr, 0, pad); + addr += pad; + + bhdr->b_size += ent_size; + bhdr->b_records++; + return bhdr; +} + +static inline Elf_Bhdr *add_note_string(Elf_Bhdr *bhdr, const char *name, + unsigned type, const char *desc) +{ + return add_boot_note(bhdr, name, type, desc, strlen(desc) + 1); +} + +static Elf_Bhdr *build_boot_notes(struct sys_info *info, const char *cmdline) +{ + Elf_Bhdr *bhdr; + + bhdr = allot(256); + bhdr->b_signature = ELF_BHDR_MAGIC; + bhdr->b_size = sizeof *bhdr; + bhdr->b_checksum = 256; /* XXX cache the current buffer size here */ + bhdr->b_records = 0; + + if (info->firmware) + bhdr = add_note_string(bhdr, NULL, EBN_FIRMWARE_TYPE, info->firmware); + bhdr = add_note_string(bhdr, NULL, EBN_BOOTLOADER_NAME, program_name); + bhdr = add_note_string(bhdr, NULL, EBN_BOOTLOADER_VERSION, program_version); + if (cmdline) + bhdr = add_note_string(bhdr, NULL, EBN_COMMAND_LINE, cmdline); + if (!bhdr) + return bhdr; + bhdr->b_checksum = 0; + bhdr->b_checksum = ipchksum(bhdr, bhdr->b_size); + return bhdr; +} + +int elf_load(struct sys_info *info, const char *filename, const char *cmdline) +{ + Elf_ehdr ehdr; + Elf_phdr *phdr = NULL; + unsigned long phdr_size; + unsigned long checksum_offset; + unsigned short checksum; + Elf_Bhdr *boot_notes = NULL; + int retval = -1; + int image_retval; + + image_name = image_version = 0; + + if (!file_open(filename)) + goto out; + + if (file_read(&ehdr, sizeof ehdr) != sizeof ehdr) { + debug("Can't read ELF header\n"); + retval = LOADER_NOT_SUPPORT; + goto out; + } + + if (ehdr.e_ident[EI_MAG0] != ELFMAG0 + || ehdr.e_ident[EI_MAG1] != ELFMAG1 + || ehdr.e_ident[EI_MAG2] != ELFMAG2 + || ehdr.e_ident[EI_MAG3] != ELFMAG3 + || ehdr.e_ident[EI_CLASS] != ARCH_ELF_CLASS + || ehdr.e_ident[EI_DATA] != ARCH_ELF_DATA + || ehdr.e_ident[EI_VERSION] != EV_CURRENT + || ehdr.e_type != ET_EXEC + || !ARCH_ELF_MACHINE_OK(ehdr.e_machine) + || ehdr.e_version != EV_CURRENT + || ehdr.e_phentsize != sizeof(Elf_phdr)) { + debug("Not a bootable ELF image\n"); + retval = LOADER_NOT_SUPPORT; + goto out; + } + + phdr_size = ehdr.e_phnum * sizeof *phdr; + phdr = allot(phdr_size);//hack LYH otherwise some one clear the last entry + file_seek(ehdr.e_phoff); + if (file_read(phdr, phdr_size) != phdr_size) { + printf("Can't read program header\n"); + goto out; + } + + if (!check_mem_ranges(info, phdr, ehdr.e_phnum)) + goto out; + + checksum_offset = process_image_notes(phdr, ehdr.e_phnum, &checksum); + + printf("Loading %s", image_name ? image_name : "image"); + if (image_version) + printf(" version %s", image_version); + printf("...\n"); + + if (!load_segments(phdr, ehdr.e_phnum, checksum_offset)) + goto out; + + if (checksum_offset) { + if (!verify_image(&ehdr, phdr, ehdr.e_phnum, checksum)) + goto out; + } + + boot_notes = build_boot_notes(info, cmdline); + + debug("current time: %x\n", currticks()); + + debug("entry point is %#x\n", ehdr.e_entry); + printf("Jumping to entry point...\n"); + + image_retval = start_elf(ehdr.e_entry, virt_to_phys(boot_notes)); +#if 0 + console_init(); +#endif + + printf("Image returned with return value %#x\n", image_retval); + retval = 0; + +out: + if (phdr) + forget(phdr); + if (boot_notes) + forget(boot_notes); + if (image_name) + forget(image_name); + if (image_version) + forget(image_version); + return retval; +} diff --git a/src/filo/main/elfnote.c b/src/filo/main/elfnote.c new file mode 100644 index 00000000..2100ec95 --- /dev/null +++ b/src/filo/main/elfnote.c @@ -0,0 +1,153 @@ +/* Support for ELF Boot Proposal as a boot image */ + +#include +#include +#include +#include + +#include "version.h" +#define DEBUG_THIS DEBUG_ELFNOTE +#include + +/* ELF image notes provide information to the loader who boots us */ + +/* This compiles and generates correct PT_NOTE segment for me. + * If it doesn't, use assembly version below. */ + +struct elf_image_note { + Elf_Nhdr hdr0; + char name0[sizeof(ELF_NOTE_BOOT)]; + char prog_name[sizeof(PROGRAM_NAME)]; + + Elf_Nhdr hdr1; + char name1[sizeof(ELF_NOTE_BOOT)]; + char version[sizeof(PROGRAM_VERSION)]; + + Elf_Nhdr hdr2; + char name2[sizeof(ELF_NOTE_BOOT)]; + unsigned short checksum; +}; + +const struct elf_image_note elf_image_notes + __attribute__ ((section (".note.ELFBoot"))) = +{ + .hdr0 = { + .n_namesz = sizeof(ELF_NOTE_BOOT), + .n_descsz = sizeof(PROGRAM_NAME), + .n_type = EIN_PROGRAM_NAME, + }, + .name0 = ELF_NOTE_BOOT, + .prog_name = PROGRAM_NAME, + + .hdr1 = { + .n_namesz = sizeof(ELF_NOTE_BOOT), + .n_descsz = sizeof(PROGRAM_VERSION), + .n_type = EIN_PROGRAM_VERSION, + }, + .name1 = ELF_NOTE_BOOT, + .version = PROGRAM_VERSION, + + .hdr2 = { + .n_namesz = sizeof(ELF_NOTE_BOOT), + .n_descsz = sizeof(unsigned short), + .n_type = EIN_PROGRAM_CHECKSUM, + }, + .name2 = ELF_NOTE_BOOT, + .checksum = 0, /* to be computed by external tool */ +}; + +/* This is refered by other files */ +const char *program_name = elf_image_notes.prog_name; +const char *program_version = elf_image_notes.version; + +#if 0 + + /* This tells the linker to make a PT_NOTE segment. + * If the section is named just ".note", it will be + * mixed up with useless .version notes generated by GCC. + */ + .section ".note.ELFBoot", "a" + + .align 4 + .int 2f - 1f + .int 4f - 3f + .int EIN_PROGRAM_NAME +1: .asciz "ELFBoot" +2: .align 4 +3: .asciz PROGRAM_NAME +4: + + .align 4 + .int 2f - 1f + .int 4f - 3f + .int EIN_PROGRAM_VERSION +1: .asciz "ELFBoot" +2: .align 4 +3: .asciz PROGRAM_VERSION +4: + + .align 4 + .int 2f - 1f + .int 4f - 3f + .int EIN_PROGRAM_CHECKSUM +1: .asciz "ELFBoot" +2: .align 4 +3: .short 0 +4: +#endif + +/* Collect information from the ELF bootloader + * Note that we have to copy them to our own memory, + * otherwise they might be overwritten afterward. */ +void collect_elfboot_info(struct sys_info *info) +{ + Elf_Bhdr *hdr = 0; + char *addr, *end; + Elf_Nhdr *nhdr; + char *name, *desc; + + if (info->boot_type == ELF_BHDR_MAGIC) + hdr = phys_to_virt(info->boot_data); + else + hdr = phys_to_virt(info->boot_arg); + + if (hdr->b_signature != ELF_BHDR_MAGIC) + return; + + if (ipchksum(hdr, hdr->b_size) != 0) { + printf("Broken ELF boot notes\n"); + return; + } + + addr = (char *) (hdr + 1); + end = addr + hdr->b_size; + while (addr < end) { + nhdr = (Elf_Nhdr *) addr; + addr += sizeof(Elf_Nhdr); + name = addr; + addr += (nhdr->n_namesz + 3) & ~3; + desc = addr; + addr += (nhdr->n_descsz + 3) & ~3; + + if (nhdr->n_namesz == 0) { + /* Standard notes */ + switch (nhdr->n_type) { + case EBN_FIRMWARE_TYPE: + info->firmware = strdup(desc); + break; + case EBN_BOOTLOADER_NAME: + debug("Bootloader: %s\n", desc); + break; + case EBN_BOOTLOADER_VERSION: + debug("Version: %s\n", desc); + break; + case EBN_COMMAND_LINE: + info->command_line = strdup(desc); + break; + case EBN_LOADED_IMAGE: + debug("Image name: %s\n", desc); + break; + } + } + } +} diff --git a/src/filo/main/filo_x.c b/src/filo/main/filo_x.c new file mode 100644 index 00000000..6569c413 --- /dev/null +++ b/src/filo/main/filo_x.c @@ -0,0 +1,129 @@ +#include + +#include +#include +#include + +#define ENTER '\r' +#define ESCAPE '\x1b' + +#ifndef AUTOBOOT_FILE +#define autoboot() ((void) 0) /* nop */ +#endif + +#ifndef AUTOBOOT_DELAY +#define autoboot_delay() 0 /* success */ +#endif + +struct sys_info sys_info; + +static void init(void) +{ + collect_sys_info(&sys_info); + + printf("%s version %s\n", program_name, program_version); + +} + +static void boot(const char *line) +{ + char file[256], *param; + + /* Split filename and parameter */ + memcpy(file, line,256); +// file = strdup(line); + param = strchr(file, ' '); + if (param) { + *param = '\0'; + param++; + } + if (elf_load(&sys_info, file, param) == LOADER_NOT_SUPPORT){ + if (linux_load(&sys_info, file, param) == LOADER_NOT_SUPPORT) + printf("Unsupported image format\n"); + } +// free(file); +} + +#ifdef AUTOBOOT_FILE +#if AUTOBOOT_DELAY + +static inline int autoboot_delay(void) +{ + unsigned int timeout; + int sec, tmp; + int key; + + key = 0; + + printf("Press for default boot, or for boot prompt... "); + for (sec = AUTOBOOT_DELAY; sec>0 && key==0; sec--) { + printf("%d", sec); + timeout = currticks() + TICKS_PER_SEC; + while (currticks() < timeout) { + if (iskey()) { + key = getchar(); + if (key==ENTER || key==ESCAPE) + break; + } + } + for (tmp = sec; tmp; tmp /= 10) + printf("\b \b"); + } + if (key == 0) { + printf("timed out\n"); + return 0; /* success */ + } else { + putchar('\n'); + if (key == ESCAPE) + return -1; /* canceled */ + else + return 0; /* default accepted */ + } +} +#endif /* AUTOBOOT_DELAY */ + +static void autoboot(void) +{ + /* If Escape key is pressed already, skip autoboot */ + if (iskey() && getchar()==ESCAPE) + return; + + if (autoboot_delay()==0) { + printf("boot: %s\n", AUTOBOOT_FILE); + boot(AUTOBOOT_FILE); + } +} +#endif /* AUTOBOOT_FILE */ + +/* The main routine */ +int filo(void) +{ + char line[256]; + + /* Initialize */ + + init(); + + /* Try default image */ + autoboot(); + + /* The above didn't work, ask user */ + while (iskey()) + getchar(); +#ifdef AUTOBOOT_FILE + strncpy(line, AUTOBOOT_FILE, sizeof(line)-1); + line[sizeof(line)-1] = '\0'; +#else + line[0] = '\0'; +#endif + for (;;) { + printf("boot: "); + getline(line, sizeof line); +// BY LYH add "quit" to exit filo + if (strcmp(line,"quit")==0) break; +// if (memcmp(line,"quit",4)==0) break; + if (line[0]) + boot(line); + } + return 0; +} diff --git a/src/filo/main/lib.c b/src/filo/main/lib.c new file mode 100644 index 00000000..6e5020c7 --- /dev/null +++ b/src/filo/main/lib.c @@ -0,0 +1,56 @@ + +#include +#include + +char *strdup(const char *s) +{ + size_t sz = strlen(s) + 1; + char *d = allot(sz); + memcpy(d, s, sz); + return d; +} + +int isspace(int c) +{ + switch (c) { + case ' ': case '\f': case '\n': + case '\r': case '\t': case '\v': + return 1; + default: + return 0; + } +} + +unsigned int get_le32(const unsigned char *p) +{ + return ((unsigned int) p[0] << 0) + | ((unsigned int) p[1] << 8) + | ((unsigned int) p[2] << 16) + | ((unsigned int) p[3] << 24); +} + +unsigned int get_le16(const unsigned char *p) +{ + return ((unsigned int) p[0] << 0) + | ((unsigned int) p[1] << 8); +} +#if (DEBUG_ALL || DEBUG_ELFBOOT || DEBUG_ELFNOTE || DEBUG_LINUXBIOS || \ + DEBUG_MALLOC || DEBUG_MULTIBOOT || DEBUG_SEGMENT || DEBUG_SYS_INFO ||\ + DEBUG_TIMER || DEBUG_BLOCKDEV || DEBUG_PCI || DEBUG_LINUXLOAD ||\ + DEBUG_IDE || DEBUG_ELTORITO) + +// It is needed by debug for filo +void hexdump(const void *p, unsigned int len) +{ + int i; + const unsigned char *q = p; + + for (i = 0; i < len; i++) { + if (i%16==0) + printf("%04x: ", i); + printf("%02x%c", q[i], i%16==15 ? '\n' : i%8==7 ? '-' : ' '); + } + if (i%16 != 0) + putchar('\n'); +} +#endif diff --git a/src/filo/main/linuxbios_x.c b/src/filo/main/linuxbios_x.c new file mode 100644 index 00000000..37f9e3f1 --- /dev/null +++ b/src/filo/main/linuxbios_x.c @@ -0,0 +1,124 @@ +/* Adapted from Etherboot 5.1.8 */ +#include +#define DEBUG_THIS DEBUG_LINUXBIOS +#include + +#if 0 + +#define for_each_lbrec(head, rec) \ + for(rec = (struct lb_record *)(((char *)head) + sizeof(*head)); \ + (((char *)rec) < (((char *)head) + sizeof(*head) + head->table_bytes)) && \ + (rec->size >= 1) && \ + ((((char *)rec) + rec->size) <= (((char *)head) + sizeof(*head) + head->table_bytes)); \ + rec = (struct lb_record *)(((char *)rec) + rec->size)) + +static void convert_memmap(struct lb_memory *lbmem, struct sys_info *info) +{ + int lbcount; + int i; + + lbcount = lbmem->size / sizeof(struct lb_memory_range); + info->memrange = malloc(lbcount * sizeof(struct memrange)); + info->n_memranges = 0; + for (i = 0; i < lbcount; i++) { + debug("%#016Lx %#016Lx %d\n", + lbmem->map[i].start, lbmem->map[i].size, + (int) lbmem->map[i].type); + if (lbmem->map[i].type != LB_MEM_RAM) + continue; + info->memrange[info->n_memranges].base = lbmem->map[i].start; + info->memrange[info->n_memranges].size = lbmem->map[i].size; + info->n_memranges++; + } +} +static int read_lbtable(struct lb_header *head, struct sys_info *info) +{ + int retval = 0; + + /* Read linuxbios tables... */ + struct lb_record *rec; + + for_each_lbrec(head, rec) { + switch(rec->tag) { + case LB_TAG_MEMORY: + convert_memmap((struct lb_memory *) rec, info); + retval = 1; + break; + }; + } + return retval; +} +#endif +#if 0 +//Use func in Etherboot +static unsigned long count_lb_records(void *start, unsigned long length) +{ + struct lb_record *rec; + void *end; + unsigned long count; + count = 0; + end = ((char *)start) + length; + for(rec = start; ((void *)rec < end) && + ((signed long)rec->size <= (end - (void *)rec)); + rec = (void *)(((char *)rec) + rec->size)) { + count++; + } + return count; +} +static int find_lb_table(void *start, void *end, struct lb_header **result) +{ + unsigned char *ptr; + /* For now be stupid.... */ + for(ptr = start; (void *)ptr < end; ptr += 16) { + struct lb_header *head = (struct lb_header *)ptr; + if ( (head->signature[0] != 'L') || + (head->signature[1] != 'B') || + (head->signature[2] != 'I') || + (head->signature[3] != 'O')) { + continue; + } + if (head->header_bytes != sizeof(*head)) + continue; + debug("Found canidate at: %p\n", head); + if (ipchksum((uint16_t *)head, sizeof(*head)) != 0) + continue; + debug("header checksum o.k.\n"); + if (ipchksum((uint16_t *)(ptr + sizeof(*head)), head->table_bytes) != + head->table_checksum) { + continue; + } + debug("table checksum o.k.\n"); + if (count_lb_records(ptr + sizeof(*head), head->table_bytes) != + head->table_entries) { + continue; + } + debug("record count o.k.\n"); + *result = head; + return 1; + }; + return 0; +} +#endif +void collect_linuxbios_info(struct sys_info *info) +{ +#if 0 + struct lb_header *lb_table; + int found; + debug("Searching for LinuxBIOS tables...\n"); + found = 0; + if (!found) { + found = find_lb_table(phys_to_virt(0x00000), phys_to_virt(0x01000), &lb_table); + } + if (!found) { + found = find_lb_table(phys_to_virt(0xf0000), phys_to_virt(0x100000), &lb_table); + } + if (!found) + return; + + debug("Found LinuxBIOS table at: %p\n", lb_table); +#endif + info->firmware = "LinuxBIOS"; +#if 0 + read_lbtable(lb_table, info); +#endif +} diff --git a/src/filo/main/malloc_x.c b/src/filo/main/malloc_x.c new file mode 100644 index 00000000..99b309aa --- /dev/null +++ b/src/filo/main/malloc_x.c @@ -0,0 +1,45 @@ +#include + +#include + +void *calloc(size_t nmemb, size_t size) +{ + size_t alloc_size = nmemb * size; + void *mem; + + if (alloc_size < nmemb || alloc_size < size) { + printf("calloc overflow: %u, %u\n", nmemb, size); + return 0; + } + + mem = allot(alloc_size); + memset(mem, 0, alloc_size); + + return mem; +} + +void *realloc(void *mem, size_t size) +{ + size_t copy_size; + void *new_mem; + size_t *mark, addr; + + if (mem == 0) + return allot(size); + if (size == 0) { + forget(mem); + return 0; + } + + addr = virt_to_phys(mem); + mark = phys_to_virt(addr - sizeof(size_t)); + copy_size = *mark; + + if (size < copy_size) + copy_size = size; + /* XXX should optimze this */ + new_mem = allot(size); + memcpy(new_mem, mem, copy_size); + forget(mem); + return new_mem; +} diff --git a/src/filo/main/pci_x.c b/src/filo/main/pci_x.c new file mode 100644 index 00000000..0c88dff7 --- /dev/null +++ b/src/filo/main/pci_x.c @@ -0,0 +1,124 @@ +#ifdef CONFIG_PCI + +/* + * 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 +#include +#include + +#define DEBUG_THIS DEBUG_PCI + +#include + +struct pci_device *dev_list; +int n_devs; + +static void pci_scan_bus(void) +{ + + unsigned int first_bus, first_devfn; + unsigned int devfn, bus, buses; + + uint32_t class; + uint16_t vendor, dev_id; + uint8_t hdr_type; + + + first_bus = 0; + first_devfn = 0; + + buses=256; + for (bus = first_bus; bus < buses; ++bus) { + for (devfn = first_devfn; devfn < 0xff; ++devfn) { + +#if 1 + if (PCI_FUNC(devfn) == 0) + pcibios_read_config_byte(bus, devfn, PCI_HEADER_TYPE, &hdr_type); + else if (!(hdr_type & 0x80)) /* not a multi-function device */ + continue; +#endif + + pcibios_read_config_word(bus,devfn, PCI_VENDOR_ID, &vendor); + if (vendor==0xffff || vendor==0) + continue; + + if (dev_list) { + dev_list[n_devs].bus = bus; + dev_list[n_devs].devfn = devfn; + dev_list[n_devs].vendor = vendor; + + pcibios_read_config_word(bus,devfn, PCI_DEVICE_ID, &dev_id); + dev_list[n_devs].dev_id = dev_id; + + pcibios_read_config_dword(bus,devfn, PCI_CLASS_REVISION, &class); + dev_list[n_devs].class = class; + + } + n_devs++; + } + first_devfn = 0; + } + first_bus = 0; + +} +#define DEBUG 0 + +void pci_init(void) +{ + /* Count devices */ + dev_list = 0; + n_devs = 0; + debug("Scanning PCI: "); + pci_scan_bus(); + debug("found %d devices\n", n_devs); + + /* Make the list */ + dev_list = allot(n_devs * sizeof(struct pci_device)); + n_devs = 0; + pci_scan_bus(); +#if DEBUG + { + int i; + for (i = 0; i < n_devs; i++) { + printf("%02x:%02x.%x %04x:%04x %04x %02x\n", + dev_list[i].bus, + PCI_SLOT(dev_list[i].devfn), + PCI_FUNC(dev_list[i].devfn), + dev_list[i].vendor, + dev_list[i].dev_id,((dev_list[i].class)>>16), + ((dev_list[i].class)>>8 & 0xff)); + } + } +#endif +} + +struct pci_device *pci_find_device_2(int vendor, int device, int devclass, int devclass2, int prog_if, int index) +{ + int i; + + for (i=0; i>16) || devclass2==((dev_list[i].class)>>16)) + if (prog_if < 0 || prog_if==((dev_list[i].class)>>8 & 0xff)) { + if (index == 0) + return &dev_list[i]; + index--; + } + } + + return NULL; +} + + +struct pci_device *pci_find_device(int vendor, int device, int devclass, int prog_if, int index) +{ + return pci_find_device_2(vendor, device, devclass, devclass, prog_if, index); +} + +#endif diff --git a/src/filo/main/printf_x.c b/src/filo/main/printf_x.c new file mode 100644 index 00000000..517e2dd2 --- /dev/null +++ b/src/filo/main/printf_x.c @@ -0,0 +1,400 @@ +/* Adapted from LinuxBIOS */ + +/* + * + * linux/lib/vsprintf.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + */ + +/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */ +/* + * Wirzenius wrote this portably, Torvalds fucked it up :-) + */ + +#include "etherboot.h" +#include +#include + +/* haha, don't need ctype.c */ +#define isdigit(c) ((c) >= '0' && (c) <= '9') +#define is_digit isdigit +#define isxdigit(c) (((c) >= '0' && (c) <= '9') || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F')) +#define islower(c) ((c) >= 'a' && (c) <= 'z') +#define toupper(c) __toupper(c) + +static inline unsigned char __toupper(unsigned char c) +{ + if (islower(c)) + c -= 'a'-'A'; + return c; +} + + +unsigned long long simple_strtoull(const char *cp,char **endp,unsigned int base) +{ + unsigned long long result = 0,value; + + if (!base) { + base = 10; + if (*cp == '0') { + base = 8; + cp++; + if ((*cp == 'x') && isxdigit(cp[1])) { + cp++; + base = 16; + } + } + } + while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp) + ? toupper(*cp) : *cp)-'A'+10) < base) { + result = result*base + value; + cp++; + } + if (endp) + *endp = (char *)cp; + return result; +} + +long long simple_strtoll(const char *cp,char **endp,unsigned int base) +{ + if(*cp=='-') + return -simple_strtoull(cp+1,endp,base); + return simple_strtoull(cp,endp,base); +} + +unsigned long long strtoull_with_suffix(const char *cp,char **endp,unsigned int base) +{ + unsigned long long result; + + if (!endp) { + printf("%s must be called with endp\n", __FUNCTION__); + return 0; + } + result = simple_strtoull(cp, endp, base); + switch (toupper(**endp)) { + case 'K': + result <<= 10; + ++*endp; + break; + case 'M': + result <<= 20; + ++*endp; + break; + case 'G': + result <<= 30; + ++*endp; + break; + } + return result; +} + +#if 0 +// it can be used to substitute the vsprintf.c in etherboot. And it has better +// support in numeric output suppot +//When you want to debug filo, you need to enable it and disable vsprintf.c +// to get output from filo +// BY LYH + +static int skip_atoi(const char **s) +{ + int i=0; + + while (is_digit(**s)) + i = i*10 + *((*s)++) - '0'; + return i; +} + +#define ZEROPAD 1 /* pad with zero */ +#define SIGN 2 /* unsigned/signed long */ +#define PLUS 4 /* show plus */ +#define SPACE 8 /* space if plus */ +#define LEFT 16 /* left justified */ +#define SPECIAL 32 /* 0x */ +#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */ + +#define do_div(n,base) ({ \ +int __res; \ +__res = ((unsigned long) n) % (unsigned) base; \ +n = ((unsigned long) n) / (unsigned) base; \ +__res; }) + +static int number(int (*tx_byte)(int byte), long num, int base, int size, int precision + ,int type) +{ + char c,sign,tmp[66]; + const char *digits="0123456789abcdefghijklmnopqrstuvwxyz"; + int i; + int count = 0; + + if (type & LARGE) + digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + if (type & LEFT) + type &= ~ZEROPAD; + if (base < 2 || base > 36) + return 0; + c = (type & ZEROPAD) ? '0' : ' '; + sign = 0; + if (type & SIGN) { + if (num < 0) { + sign = '-'; + num = -num; + size--; + } else if (type & PLUS) { + sign = '+'; + size--; + } else if (type & SPACE) { + sign = ' '; + size--; + } + } + if (type & SPECIAL) { + if (base == 16) + size -= 2; + else if (base == 8) + size--; + } + i = 0; + if (num == 0) + tmp[i++]='0'; + else while (num != 0) + tmp[i++] = digits[do_div(num,base)]; + if (i > precision) + precision = i; + size -= precision; + if (!(type&(ZEROPAD+LEFT))) + while(size-->0) + tx_byte(' '), count++; + if (sign) + tx_byte(sign), count++; + if (type & SPECIAL) { + if (base==8) + tx_byte('0'), count++; + else if (base==16) { + tx_byte('0'), count++; + tx_byte(digits[33]), count++; + } + } + if (!(type & LEFT)) + while (size-- > 0) + tx_byte(c), count++; + while (i < precision--) + tx_byte('0'), count++; + while (i-- > 0) + tx_byte(tmp[i]), count++; + while (size-- > 0) + tx_byte(' '), count++; + return count; +} + + +int vtxprintf(int (*tx_byte)(int byte), const char *fmt, va_list args) +{ + int len; + unsigned long num; + int i, base; + const char *s; + + int flags; /* flags to number() */ + + int field_width; /* width of output field */ + int precision; /* min. # of digits for integers; max + number of chars for from string */ + int qualifier; /* 'h', 'l', or 'L' for integer fields */ + + int count; + + for (count=0; *fmt ; ++fmt) { + if (*fmt != '%') { + tx_byte(*fmt), count++; + continue; + } + + /* process flags */ + flags = 0; + repeat: + ++fmt; /* this also skips first '%' */ + switch (*fmt) { + case '-': flags |= LEFT; goto repeat; + case '+': flags |= PLUS; goto repeat; + case ' ': flags |= SPACE; goto repeat; + case '#': flags |= SPECIAL; goto repeat; + case '0': flags |= ZEROPAD; goto repeat; + } + + /* get field width */ + field_width = -1; + if (is_digit(*fmt)) + field_width = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + field_width = va_arg(args, int); + if (field_width < 0) { + field_width = -field_width; + flags |= LEFT; + } + } + + /* get the precision */ + precision = -1; + if (*fmt == '.') { + ++fmt; + if (is_digit(*fmt)) + precision = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + precision = va_arg(args, int); + } + if (precision < 0) + precision = 0; + } + + /* get the conversion qualifier */ + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') { + qualifier = *fmt; + ++fmt; + } + + /* default base */ + base = 10; + + switch (*fmt) { + case 'c': + if (!(flags & LEFT)) + while (--field_width > 0) + tx_byte(' '), count++; + tx_byte((unsigned char) va_arg(args, int)), count++; + while (--field_width > 0) + tx_byte(' '), count++; + continue; + + case 's': + s = va_arg(args, char *); + if (!s) + s = ""; + + len = strnlen(s, precision); + + if (!(flags & LEFT)) + while (len < field_width--) + tx_byte(' '), count++; + for (i = 0; i < len; ++i) + tx_byte(*s++), count++; + while (len < field_width--) + tx_byte(' '), count++; + continue; + + case 'p': + if (field_width == -1) { + field_width = 2*sizeof(void *); + flags |= ZEROPAD; + } + count += number(tx_byte, + (unsigned long) va_arg(args, void *), 16, + field_width, precision, flags); + continue; + + + case 'n': + if (qualifier == 'l') { + long * ip = va_arg(args, long *); + *ip = count; + } else { + int * ip = va_arg(args, int *); + *ip = count; + } + continue; + + case '%': + tx_byte('%'), count++; + continue; + + /* integer number formats - set up the flags and "break" */ + case 'o': + base = 8; + break; + + case 'X': + flags |= LARGE; + case 'x': + base = 16; + break; + + case 'd': + case 'i': + flags |= SIGN; + case 'u': + break; + + default: + tx_byte('%'), count++; + if (*fmt) + tx_byte(*fmt), count++; + else + --fmt; + continue; + } + if (qualifier == 'L') + num = va_arg(args, unsigned long long); + else if (qualifier == 'l') + num = va_arg(args, unsigned long); + else if (qualifier == 'h') { + num = (unsigned short) va_arg(args, int); + if (flags & SIGN) + num = (short) num; + } else if (flags & SIGN) + num = va_arg(args, int); + else + num = va_arg(args, unsigned int); + count += number(tx_byte, num, base, field_width, precision, flags); + } + return count; +} + +/* FIXME this global makes vsprintf non-reentrant + */ +static char *str_buf; +static int str_tx_byte(int byte) +{ + *str_buf = byte; + str_buf++; + return byte; +} + +int vsprintf(char * buf, const char *fmt, va_list args) +{ + int i; + str_buf = buf; + i = vtxprintf(str_tx_byte, fmt, args); + /* maeder/Ispiri -- The null termination was missing a deference */ + /* and was just zeroing out the pointer instead */ + *str_buf = '\0'; + return i; +} + +int sprintf(char * buf, const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i=vsprintf(buf,fmt,args); + va_end(args); + return i; +} + +void printf(const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i = vtxprintf(putchar, fmt, args); + va_end(args); + return i; +} +#endif diff --git a/src/filo/usb/debug_x.c b/src/filo/usb/debug_x.c new file mode 100644 index 00000000..95a80b0a --- /dev/null +++ b/src/filo/usb/debug_x.c @@ -0,0 +1,433 @@ +#ifdef USB_DISK +/******************************************************************************* + * + * + * Copyright 2003 Steven James and + * LinuxLabs http://www.linuxlabs.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. + * + ******************************************************************************/ + +#include +#include +#include +#include + +#define DEBUG_THIS DEBUG_USB +#include + +#define DPRINTF debug + +#include "usb.h" +#include "uhci.h" +#include "debug_x.h" + +//#include + + +void dump_link( link_pointer_t *link, char *prefix) +{ + DPRINTF("%saddr: %08x\n", prefix, MEM_ADDR(link->link) ); + DPRINTF("%s raw addr: %04x\n", prefix, (link->link) <<4 ); + DPRINTF("%sterminate: %x\n", prefix, link->terminate); + DPRINTF("%squeue: %x\n", prefix, link->queue); + DPRINTF("%sdepth: %x\n", prefix, link->depth); +} + +void dump_frame_list( link_pointer_t *addr, char *prefix) +{ + int i; + + DPRINTF("%sFRAMELIST:\n",prefix); + + for(i=0;i<10; i++) { + dump_link(addr+i, prefix); + if(addr[i].queue) + dump_queue_head( MEM_ADDR(addr[i].link), ""); + else + dump_td( MEM_ADDR(addr[i].link), ""); + } +} + +void dump_hex(uchar *data, int len, char *prefix) +{ + int i=0; + + while(ipacket_type) { + case SETUP_TOKEN: + DPRINTF("%stype: SETUP\n", prefix); + break; + case OUT_TOKEN: + DPRINTF("%stype: OUT\n", prefix); + break; + case IN_TOKEN: + DPRINTF("%stype: IN\n", prefix); + break; + default: + DPRINTF("%stype: INVALID (%02x)\n", prefix, td->packet_type); + break; + } + + DPRINTF("%sretries: %x\n", prefix, td->retrys); + + if(td->isochronous) { + DPRINTF("%sisochronous\n", prefix); + } + + if(td->interrupt) { + DPRINTF("%sIOC\n", prefix); + } + + if(td->lowspeed) { + DPRINTF("%slowspeed\n", prefix); + } + + if(td->detect_short) { + DPRINTF("%sDETECT_SHORT\n", prefix); + } + + DPRINTF("%sactive: %04x\n", prefix, td->active); + DPRINTF("%sdevice_addr: %02x\n", prefix, td->device_addr); + DPRINTF("%sendpoint: %1x\n", prefix, td->endpoint); + DPRINTF("%sdata_toggle: %1x\n", prefix, td->data_toggle); + DPRINTF("%smax_transfer: %x\n", prefix, td->max_transfer); + DPRINTF("%sactual: %x\n", prefix, td->actual); + DPRINTF("%slink:\n", prefix); + + if(td->stall) { + DPRINTF("%sSTALL\n", prefix); + } + + if(td->bitstuff) { + DPRINTF("%sBITSTUFF ERROR\n", prefix); + } + + if(td->crc) { + DPRINTF("%sCRC ERROR\n", prefix); + } + + if(td->nak) { + DPRINTF("%sNAK ERROR\n", prefix); + } + + if(td->babble) { + DPRINTF("%sBABBLE ERROR\n", prefix); + } + + if(td->buffer_error) { + DPRINTF("%sBUFFER ERROR\n", prefix); + } + + if(MEM_ADDR(td->link.link) == td) { + DPRINTF("link loops back to me!\n"); + return; + } + + dump_link(&(td->link), newpre); + if(!td->link.terminate) { + if(td->link.queue) + dump_queue_head(MEM_ADDR(td->link.link), prefix ); + else + dump_td(MEM_ADDR(td->link.link), prefix); + } +} + +void dump_queue_head( queue_head_t *qh, char *prefix) +{ + char newpre[64]; + + newpre[0] = '\t'; + strcpy(newpre+1, prefix); + + DPRINTF("%sQUEUE HEAD(%x):\n", prefix, qh); + DPRINTF("%sdepth:\n", prefix); + dump_link( &(qh->depth), newpre); + + if(!qh->depth.terminate) { + if(qh->depth.queue) { + dump_queue_head(MEM_ADDR(qh->depth.link), newpre); + } + else { + dump_td( MEM_ADDR(qh->depth.link), newpre); + } + } + + + DPRINTF("%sbredth:\n", prefix); + dump_link( &(qh->bredth), newpre); + if(!qh->bredth.terminate) { + if(qh->bredth.queue) { + dump_queue_head(MEM_ADDR(qh->bredth.link), newpre); + } + else { + dump_td( MEM_ADDR(qh->bredth.link), newpre); + } + } +} + +void dump_transaction( transaction_t *trans, char *prefix) +{ + char newpre[64]; + + newpre[0] = '\t'; + strcpy(newpre+1, prefix); + + + DPRINTF("%s TRANSACTION(%x):\n", prefix, trans); + dump_queue_head( trans->qh, newpre); + + DPRINTF("%s TDs:\n", prefix); + dump_td( trans->td_list, newpre); + + DPRINTF("\n"); + if(trans->next) + dump_transaction(trans->next, prefix); +} + +void dump_usbdev( usbdev_t *dev, char *prefix) +{ + char newpre[64]; + int i; + + newpre[0] = '\t'; + strcpy(newpre+1, prefix); + + DPRINTF("%saddress: %x\n", prefix, dev->address); + DPRINTF("%sclass: %x\n", prefix, dev->class); + DPRINTF("%ssubclass: %x\n", prefix, dev->subclass); + DPRINTF("%sbulk_in: %x\n", prefix, dev->bulk_in); + DPRINTF("%sbulk_out: %x\n", prefix, dev->bulk_out); + DPRINTF("%sinterrupt: %x\n", prefix, dev->interrupt); + + DPRINTF("%sep toggle:\n", prefix); + for(i=0;itoggle[i]); + } + + DPRINTF("%sep max_packet:\n", prefix); + for(i=0;imax_packet[i]); + } +} + +void dump_all_usbdev(char *prefix) +{ + int i; + + for(i=0;ibLength); + DPRINTF("%stype: %02x\n", prefix, des->type); + DPRINTF("%sbcdVersion: %1x%1x\n", prefix, des->bcdVersion[1], des->bcdVersion[0]); + DPRINTF("%sClass: %02x\n",prefix, des->Class); + DPRINTF("%sSubClass: %02x\n",prefix, des->SubClass); + DPRINTF("%sprotocol: %02x\n",prefix, des->protocol); + DPRINTF("%smax_packet: %x\n",prefix, des->max_packet); + DPRINTF("%sidVendor: %04x\n", prefix, des->idVendor); + DPRINTF("%sidProduct: %04x\n", prefix, des->idProduct); + DPRINTF("%sbcdDeviceVersion: %1x%1x\n", prefix, des->bcdDevice[1], des->bcdDevice[0]); + DPRINTF("%siManufacturor: %02x\n", prefix, des->iManufacturor); + DPRINTF("%siProduct: %02x\n", prefix, des->iProduct); + DPRINTF("%siSerial: %02x\n", prefix, des->iSerial); + DPRINTF("%sbNumConfig: %02x\n", prefix, des->bNumConfig); + +} + +void dump_interface_descriptor( interface_descriptor_t *iface, char *prefix) +{ + + DPRINTF("%sbLength: %02x\n", prefix, iface->bLength); + DPRINTF("%stype: %02x\n", prefix, iface->type); + DPRINTF("%sbInterfaceNumber: %02x\n", prefix, iface->bInterfaceNumber); + DPRINTF("%sbAlternateSetting: %02x\n", prefix, iface->bAlternateSetting); + DPRINTF("%sbNumEndpoints: %02x\n", prefix, iface->bNumEndpoints); + DPRINTF("%sbInterfaceClass: %02x\n", prefix, iface->bInterfaceClass); + DPRINTF("%sbInterfaceSubClass: %02x\n", prefix, iface->bInterfaceSubClass); + DPRINTF("%sbInterfaceProtocol: %02x\n", prefix, iface->bInterfaceProtocol); + DPRINTF("%siInterface: %02x\n", prefix, iface->iInterface); +} + +void dump_endpoint_descriptor( endpoint_descriptor_t *ep, char *prefix) +{ + + DPRINTF("%sbLength: %02x\n", prefix, ep->bLength); + DPRINTF("%stype: %02x\n", prefix, ep->type); + DPRINTF("%sbEndpointAddress: %02x\n", prefix, ep->bEndpointAddress); + DPRINTF("%sbmAttributes: %02x\n", prefix, ep->bmAttributes); + DPRINTF("%swMaxPacketSize: %02x\n", prefix, ep->wMaxPacketSize); + DPRINTF("%sbInterval: %02x\n", prefix, ep->bInterval); +} + +void dump_config_descriptor( uchar *des, char *prefix) // YES uchar * +{ + config_descriptor_t *config; + interface_descriptor_t *iface; + endpoint_descriptor_t *ep; + char newpre[64]; + int i; + + memset(newpre,0,sizeof(newpre)); + newpre[0] = '\t'; + strcpy(newpre+1, prefix); + + config = (config_descriptor_t *) des; + iface = (interface_descriptor_t *) (des + config->bLength); + ep = (endpoint_descriptor_t *) (des + config->bLength + iface->bLength); + + // now, the config itself + DPRINTF("%sbLength: %02x\n", prefix, config->bLength); + DPRINTF("%stype: %02x\n", prefix, config->type); + DPRINTF("%swTotalLength: %04x\n", prefix, config->wTotalLength); + DPRINTF("%sbNumInterfaces: %02x\n", prefix, config->bNumInterfaces); + DPRINTF("%sbConfigurationValue: %02x\n", prefix, config->bConfigurationValue); + DPRINTF("%siConfiguration: %02x\n", prefix, config->iConfiguration); + DPRINTF("%sbmAttributes: %02x\n", prefix, config->bmAttributes); + + DPRINTF("%sbMaxPower: %02x\n", prefix, config->bMaxPower); + + DPRINTF("\n%sInterface(%x):\n", prefix, iface); + dump_interface_descriptor(iface, newpre); + + newpre[1] = '\t'; + strcpy(newpre+2, prefix); + + for(i=0; ibNumEndpoints; i++) { + DPRINTF("\n%sEndpoint (%x):\n", newpre+1, ep+i); + dump_endpoint_descriptor( ep+i, newpre); + } +} + +// Some control message bmRequestType defines +#define CTRL_DEVICE 0 +#define CONTROL_INTERFACE 1 +#define CONTROL_ENDPOINT 2 +#define CONTROL_OTHER 3 +#define CONTROL_RECIPIENT_MASK 0x1f + +#define CONTROL_TYPE_STD 0 +#define CONTROL_TYPE_CLASS 0x20 +#define CONTROL_CLASS_VENDOR 0x40 +#define CONTROL_CLASS_MASK 0x60 + +#define CONTROL_OUT 0 +#define CONTROL_IN 0x80 +#define CONTROL_DIR_MASK 0x80 + +// bRequest values +#define GET_STATUS 0 +#define CLEAR_FEATURE 1 +#define SET_FEATURE 3 +#define SET_ADDRESS 5 + +#define GET_DESCRIPTOR 6 +#define SET_DESCRIPTOR 7 + +#define GET_CONFIGURATION 8 +#define SET_CONFIGURATION 9 + +#define GET_INTERFACE 10 +#define SET_INTERFACE 11 + +#define SYNC_FRAME 12 + +// descriptor types +#define DEVICE_DESC 1 +#define CONFIGURATION_DESC 2 +#define STRING_DESC 3 +#define INTERFACE_DESC 4 +#define ENDPOINT_DESC 5 +#define OTHERSPEED_DESC 7 +#define POWER_DESC 8 + +// features +#define FEATURE_HALT 0 +void dump_ctrlmsg( ctrl_msg_t *msg, char *prefix) +{ + DPRINTF("%sbmRequestType: %02x\n", prefix, msg->bmRequestType); + DPRINTF("%sbRequest: %02x\n", prefix, msg->bRequest); + DPRINTF("%swValue: %04x\n", prefix, msg->wValue); + DPRINTF("%swIndex: %04x\n", prefix, msg->wIndex); + DPRINTF("%swLength: %04x\n", prefix, msg->wLength); +} + +#endif diff --git a/src/filo/usb/debug_x.h b/src/filo/usb/debug_x.h new file mode 100644 index 00000000..109daae0 --- /dev/null +++ b/src/filo/usb/debug_x.h @@ -0,0 +1,18 @@ +#ifndef _DEBUG_X_H +#define _DEBUG_X_H + +void dump_hex(uchar *data, int len, char *prefix); +void dump_link( link_pointer_t *link, char *prefix); +void dump_td( td_t *td, char *prefix); +void dump_queue_head( queue_head_t *qh, char *prefix); +void dump_transaction( transaction_t *trans, char *prefix); +void dump_usbdev( usbdev_t *dev, char *prefix); +void dump_uhci(uint32_t port); +//void dump_all_usbdev(char *prefix); +void dump_device_descriptor( device_descriptor_t *des, char *prefix); +void dump_interface_descriptor( interface_descriptor_t *iface, char *prefix); +void dump_endpoint_descriptor( endpoint_descriptor_t *ep, char *prefix); +void dump_config_descriptor( uchar *des, char *prefix); +void dump_ctrlmsg( ctrl_msg_t *msg, char *prefix); + +#endif diff --git a/src/filo/usb/ohci.c b/src/filo/usb/ohci.c new file mode 100644 index 00000000..5c84c637 --- /dev/null +++ b/src/filo/usb/ohci.c @@ -0,0 +1,1437 @@ +#ifdef USB_DISK + +/******************************************************************************* + * + * + * Copyright 2003 Steven James and + * LinuxLabs http://www.linuxlabs.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. + * + ******************************************************************************/ + +#include +#include +#include +#include + +#define DEBUG_THIS DEBUG_USB +#include + +#define DPRINTF debug + +#define DEBUG_TD 0 +#define DEBUG_ED 0 + + +#include "usb.h" +#include "ohci.h" + + +extern int usec_offset; + +ohci_regs_t *ohci_regs; + +// It will clear the enable bit +void ohc_clear_stat(uchar dev) +{ + uint32_t value; + ohci_regs = (ohci_regs_t *)hc_base[dev]; + + value = readl(&ohci_regs->cmdstatus); + writel(value, &ohci_regs->cmdstatus); +} + +void clear_oport_stat(uint32_t port) +{ + uint32_t value; + + value = readl(port); + writel(value, port); +} + +void oport_suspend( uint32_t port) +{ + writel( RH_PS_PSS, port); + +} +void oport_wakeup( uint32_t port) +{ + writel( RH_PS_POCI, port); + +} +#if 0 +void oport_resume( uint32_t port) +{ + uint32_t value; + + value = readl(port); + value |= 0x40; + writel(value, port); + udelay(20000+usec_offset); + value &= ~0x40; + writel(value, port); + + do { + value = readl(port); + } while(value & 0x40); +} +#endif +void oport_enable( uint32_t port) +{ + uint32_t value; + + value = readl(port); + + if((value & RH_PS_CCS)) { // if connected + writel( RH_PS_PES, port); + udelay(10); + writel( RH_PS_PESC, port); // Clear Change bit + } + +} + + + +void oport_disable( uint32_t port) +{ + writel( RH_PS_CCS, port); +} + +void oport_reset(uint32_t port) +{ + + uint32_t value; + + writel( RH_PS_PRS, port); + do { + value = readl( port ); + } while (!(value & RH_PS_PRSC) ); + writel(RH_PS_PRSC, port); //Clear Change bit + +} + +void oport_reset_long(uint32_t port) +{ + oport_reset(port); +} + +#if 0 + +int ohc_stop(uchar dev) +{ + unsigned short tmp; + uint32_t ctl; + + ohci_regs = hc_base[dev]; + + ctl = readl( &ohci_regs->control); + ctl &= ~(OHCI_CTRL_PLE|OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_IE); + writel (ctl, &ohci_regs->control); + + return(0); +} +#endif + + +#define MAX_OHCI_TD 32 + +ohci_td_t *_ohci_td; +uint8_t _ohci_td_tag[MAX_OHCI_TD]; //1: used, 0:unused + +void init_ohci_td(){ + _ohci_td = allot2(sizeof(ohci_td_t)*MAX_OHCI_TD, 0x1f); // 32 byte aligna + if(_ohci_td==0) { + printf("init_ohci_td: NOMEM\n"); + } + memset(_ohci_td_tag, 0, sizeof(_ohci_td_tag)); +} + +ohci_td_t *td_alloc(ohci_t *ohci, int memflag){ + int i; + ohci_td_t *td; + for(i = 0; i< MAX_OHCI_TD; i++ ) { + if(_ohci_td_tag[i]==1) continue; + td = &_ohci_td[i]; + memset(td, 0, sizeof(ohci_td_t)); + td->td_dma = (void *)virt_to_phys(td); + _ohci_td_tag[i] = 1; + return td; + } + printf("td_alloc: no free slot\n"); + return 0; +} + +int td_free(ohci_t *ohci, ohci_td_t *td) { + int i; + for(i = 0; i< MAX_OHCI_TD; i++ ) { + if(_ohci_td_tag[i]==0) continue; + if(&_ohci_td[i] == td ) { + _ohci_td_tag[i] = 0; + return 1; + } + } + return 0; +} + + +struct ohci_td * +dma_to_td (struct ohci * hc, void *td_dma) +{ + int i; + ohci_td_t *td; + for(i = 0; i< MAX_OHCI_TD; i++ ) { + if(_ohci_td_tag[i]==0) continue; + td = &_ohci_td[i]; + if(td->td_dma == td_dma ) { + return td; + } + } + printf("dma_to_td: can not find td\n"); + return 0; + +} + +ohci_t _ohci_x[MAX_CONTROLLERS]; + +void ohci_init(void) +{ + init_ohci_td(); +} + +static int ohci_get_current_frame_number (struct usbdev *usb_dev) +{ + ohci_t * ohci = &_ohci_x[usb_dev->controller]; + + return le16_to_cpu (ohci->hcca->frame_no); +} + + + +static u32 roothub_a (struct ohci *hc) + { return readl (&hc->regs->roothub.a); } +static inline u32 roothub_b (struct ohci *hc) + { return readl (&hc->regs->roothub.b); } +static inline u32 roothub_status (struct ohci *hc) + { return readl (&hc->regs->roothub.status); } +static u32 roothub_portstatus (struct ohci *hc, int i) + { return readl (&hc->regs->roothub.portstatus[i]);} + + +#if DEBUG_USB==1 + +#define OHCI_VERBOSE_DEBUG + +# define dbg(...) \ + do { printf(__VA_ARGS__); printf("\n"); } while (0) + +static void urb_print (struct urb * urb, char * str, int small) +{ + unsigned int pipe= urb->pipe; + + if (!urb->dev ) { + dbg("%s URB: no dev", str); + return; + } + +#ifndef OHCI_VERBOSE_DEBUG + if (urb->status != 0) +#endif + dbg("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,flags:%4x,len:%d/%d,stat:%d(%x)", + str, + ohci_get_current_frame_number (urb->dev), + usb_pipedevice (pipe), + usb_pipeendpoint (pipe), + usb_pipeout (pipe)? 'O': 'I', + usb_pipetype (pipe) < 2? (usb_pipeint (pipe)? "INTR": "ISOC"): + (usb_pipecontrol (pipe)? "CTRL": "BULK"), + urb->transfer_flags, + urb->actual_length, + urb->transfer_buffer_length, + urb->status, urb->status); +#ifdef OHCI_VERBOSE_DEBUG +// if (!small) { + int i, len; + + if (usb_pipecontrol (pipe)) { + printf ("ohci.c: cmd(8):"); + for (i = 0; i < 8 ; i++) + printf (" %02x", ((u8 *) urb->setup_packet) [i]); + printf ("\n"); + } + if (urb->transfer_buffer_length > 0 && urb->transfer_buffer) { + printf("ohci.c: data(%d/%d):", + urb->actual_length, + urb->transfer_buffer_length); + len = usb_pipeout (pipe)? + urb->transfer_buffer_length: urb->actual_length; + for (i = 0; i < 16 && i < len; i++) + printf (" %02x", ((u8 *) urb->transfer_buffer) [i]); + printf ("%s stat:%d\n", i < len? "...": "", urb->status); + } +// } +#endif +} + +/* just for debugging; prints non-empty branches of the int ed tree inclusive iso eds*/ +void ep_print_int_eds (ohci_t * ohci, char * str) { + int i, j; + u32 * ed_p; + for (i= 0; i < 32; i++) { + j = 5; + ed_p = &(ohci->hcca->int_table [i]); + if (*ed_p == 0) + continue; + printf ("ohci.c: %s branch int %2d(%2x):", str, i, i); +#if 0 + while (*ed_p != 0 && j--) { + ed_t *ed = dma_to_ed (ohci, le32_to_cpup(ed_p)); + printk (" ed: %4x;", ed->hwINFO); + ed_p = &ed->hwNextED; + } +#endif + printf ("\n"); + } +} +static void ohci_dump_intr_mask (char *label, u32 mask) +{ + dbg ("%s: 0x%08x%s%s%s%s%s%s%s%s%s", + label, + mask, + (mask & OHCI_INTR_MIE) ? " MIE" : "", + (mask & OHCI_INTR_OC) ? " OC" : "", + (mask & OHCI_INTR_RHSC) ? " RHSC" : "", + (mask & OHCI_INTR_FNO) ? " FNO" : "", + (mask & OHCI_INTR_UE) ? " UE" : "", + (mask & OHCI_INTR_RD) ? " RD" : "", + (mask & OHCI_INTR_SF) ? " SF" : "", + (mask & OHCI_INTR_WDH) ? " WDH" : "", + (mask & OHCI_INTR_SO) ? " SO" : "" + ); +} +static void maybe_print_eds (char *label, u32 value) +{ + if (value) + dbg ("%s %08x", label, value); +} +static char *hcfs2string (int state) +{ + switch (state) { + case OHCI_USB_RESET: return "reset"; + case OHCI_USB_RESUME: return "resume"; + case OHCI_USB_OPER: return "operational"; + case OHCI_USB_SUSPEND: return "suspend"; + } + return "?"; +} +// dump control and status registers +static void ohci_dump_status (ohci_t *controller) +{ + struct ohci_regs *regs = controller->regs; + u32 temp; + + temp = readl (®s->revision) & 0xff; + if (temp != 0x10) + dbg ("spec %d.%d", (temp >> 4), (temp & 0x0f)); + + temp = readl (®s->control); + dbg ("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", temp, + (temp & OHCI_CTRL_RWE) ? " RWE" : "", + (temp & OHCI_CTRL_RWC) ? " RWC" : "", + (temp & OHCI_CTRL_IR) ? " IR" : "", + hcfs2string (temp & OHCI_CTRL_HCFS), + (temp & OHCI_CTRL_BLE) ? " BLE" : "", + (temp & OHCI_CTRL_CLE) ? " CLE" : "", + (temp & OHCI_CTRL_IE) ? " IE" : "", + (temp & OHCI_CTRL_PLE) ? " PLE" : "", + temp & OHCI_CTRL_CBSR + ); + + temp = readl (®s->cmdstatus); + dbg ("cmdstatus: 0x%08x SOC=%d%s%s%s%s", temp, + (temp & OHCI_SOC) >> 16, + (temp & OHCI_OCR) ? " OCR" : "", + (temp & OHCI_BLF) ? " BLF" : "", + (temp & OHCI_CLF) ? " CLF" : "", + (temp & OHCI_HCR) ? " HCR" : "" + ); + + ohci_dump_intr_mask ("intrstatus", readl (®s->intrstatus)); + ohci_dump_intr_mask ("intrenable", readl (®s->intrenable)); + // intrdisable always same as intrenable + // ohci_dump_intr_mask ("intrdisable", readl (®s->intrdisable)); + + maybe_print_eds ("ed_periodcurrent", readl (®s->ed_periodcurrent)); + + maybe_print_eds ("ed_controlhead", readl (®s->ed_controlhead)); + maybe_print_eds ("ed_controlcurrent", readl (®s->ed_controlcurrent)); + + maybe_print_eds ("ed_bulkhead", readl (®s->ed_bulkhead)); + maybe_print_eds ("ed_bulkcurrent", readl (®s->ed_bulkcurrent)); + + maybe_print_eds ("donehead", readl (®s->donehead)); +} + +static void ohci_dump_roothub (ohci_t *controller, int verbose) +{ + u32 temp, ndp, i; + + temp = roothub_a (controller); + if (temp == ~(u32)0) + return; + ndp = (temp & RH_A_NDP); + + if (verbose) { + dbg ("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp, + ((temp & RH_A_POTPGT) >> 24) & 0xff, + (temp & RH_A_NOCP) ? " NOCP" : "", + (temp & RH_A_OCPM) ? " OCPM" : "", + (temp & RH_A_DT) ? " DT" : "", + (temp & RH_A_NPS) ? " NPS" : "", + (temp & RH_A_PSM) ? " PSM" : "", + ndp + ); + temp = roothub_b (controller); + dbg ("roothub.b: %08x PPCM=%04x DR=%04x", + temp, + (temp & RH_B_PPCM) >> 16, + (temp & RH_B_DR) + ); + temp = roothub_status (controller); + dbg ("roothub.status: %08x%s%s%s%s%s%s", + temp, + (temp & RH_HS_CRWE) ? " CRWE" : "", + (temp & RH_HS_OCIC) ? " OCIC" : "", + (temp & RH_HS_LPSC) ? " LPSC" : "", + (temp & RH_HS_DRWE) ? " DRWE" : "", + (temp & RH_HS_OCI) ? " OCI" : "", + (temp & RH_HS_LPS) ? " LPS" : "" + ); + } + + for (i = 0; i < ndp; i++) { + temp = roothub_portstatus (controller, i); + dbg ("roothub.portstatus [%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s", + i, + temp, + (temp & RH_PS_PRSC) ? " PRSC" : "", + (temp & RH_PS_OCIC) ? " OCIC" : "", + (temp & RH_PS_PSSC) ? " PSSC" : "", + (temp & RH_PS_PESC) ? " PESC" : "", + (temp & RH_PS_CSC) ? " CSC" : "", + + (temp & RH_PS_LSDA) ? " LSDA" : "", + (temp & RH_PS_PPS) ? " PPS" : "", + (temp & RH_PS_PRS) ? " PRS" : "", + (temp & RH_PS_POCI) ? " POCI" : "", + (temp & RH_PS_PSS) ? " PSS" : "", + + (temp & RH_PS_PES) ? " PES" : "", + (temp & RH_PS_CCS) ? " CCS" : "" + ); + } +} + +static void ohci_dump (ohci_t *controller, int verbose) +{ + dbg ("OHCI controller usb-%x state", controller->regs); + + // dumps some of the state we know about + ohci_dump_status (controller); + if (verbose) + ep_print_int_eds (controller, "hcca"); + dbg ("hcca frame #%04x", controller->hcca->frame_no); + ohci_dump_roothub (controller, 1); +} +void ohci_dump_x(uchar controller) +{ + ohci_t *ohci; + ohci = &_ohci_x[controller]; + ohci_dump (ohci, 1); +} +#endif + +/* link an ed into one of the HC chains */ + +/* ED is only enqueued and dequeued by HCD + So ep_link may only to be called two times for every device (function) -- ed, one for controled and one for bulked + one ohci may have several controled and bulked. +*/ + +int ep_link (ohci_t * ohci, ed_t * edi) +{ + volatile ed_t * ed = edi; + + ed->state = ED_OPER; + + switch (ed->type) { + case PIPE_CONTROL: + ed->hwNextED = 0; + if (ohci->ed_controltail == NULL) { +// debug("ep_link control 21 ed->dma = %x\n", (uint32_t)ed->dma); + writel ((uint32_t)ed->dma, &ohci->regs->ed_controlhead); + } else { +// debug("ep_link control 22 ed->dma = %x\n", (uint32_t)ed->dma); + ohci->ed_controltail->hwNextED = cpu_to_le32 ((uint32_t)ed->dma); + } + ed->ed_prev = ohci->ed_controltail; + if (!ohci->ed_controltail) { + /* enable control ed list */ + ohci->hc_control |= OHCI_CTRL_CLE; //5 + writel (ohci->hc_control, &ohci->regs->control); + } + ohci->ed_controltail = edi; + break; + + case PIPE_BULK: + ed->hwNextED = 0; + if (ohci->ed_bulktail == NULL) { + // debug("ep_link control 31 ed->dma = %x\n", (uint32_t)ed->dma); + writel ((uint32_t)ed->dma, &ohci->regs->ed_bulkhead); + } else { + // debug("ep_link control 32 ed->dma = %x\n", (uint32_t)ed->dma); + ohci->ed_bulktail->hwNextED = cpu_to_le32 ((uint32_t)ed->dma); + } + ed->ed_prev = ohci->ed_bulktail; + if (!ohci->ed_bulktail) { + /* enable bulk ed list */ + ohci->hc_control |= OHCI_CTRL_BLE; //5 + writel (ohci->hc_control, &ohci->regs->control); + } + ohci->ed_bulktail = edi; + break; + } + return 0; +} +/* add/reinit an endpoint; this should be done once at the usb_set_configuration command, + * but the USB stack is a little bit stateless so we do it at every transaction + * if the state of the ed is ED_NEW then a dummy td is added and the state is changed to ED_UNLINK + * in all other cases the state is left unchanged + * the ed info fields are setted anyway even though most of them should not change */ + +ed_t * ep_add_ed ( + usbdev_t * usb_dev, + unsigned int pipe, + int interval, + int load, + int mem_flags +) +{ + ohci_t * ohci = &_ohci_x[usb_dev->controller]; + ohci_td_t * td; + ed_t * ed; + unsigned long flags; + int i; + + /* We use preallocate ed in ohci struct + numbering rule ??? + */ + i = (usb_pipeendpoint (pipe) << 1) |(usb_pipecontrol (pipe)? 0: usb_pipeout (pipe)); + ed = (ed_t *)&ohci->ed[i]; + +// debug("ep_add_ed: usb_dev port=%x, controller = %d ohci=%x ohci->ed=%x ed=%x ed->dma=%x\n", usb_dev->port,usb_dev->controller, ohci, ohci->ed, ed, ed->dma); + + if (ed->state == ED_NEW) { + ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); /* skip ed */ + /* dummy td; end of td list for ed */ + td = td_alloc (ohci, 0); + + ed->hwTailP = cpu_to_le32 ((uint32_t)td->td_dma); + ed->hwHeadP = ed->hwTailP; + ed->state = ED_UNLINK; + ed->type = usb_pipetype (pipe); + ohci->ed_cnt++; // we will be used to calcaulate next pipe + +// debug("ep_add_ed 1 td=%x dma=%x ed->dma=%x ed->hwHeadP=%x ed->hwTailP=%x\n", td, td->td_dma, ed->dma, ed->hwHeadP, ed->hwTailP); + + } + + ohci->dev[usb_pipedevice (pipe)] = usb_dev; // marked the ed to this dev + + ed->hwINFO = cpu_to_le32 (usb_pipedevice (pipe) + | usb_pipeendpoint (pipe) << 7 + | (usb_pipeisoc (pipe)? 0x8000: 0) + | (usb_pipecontrol (pipe)? 0: (usb_pipeout (pipe)? 0x800: 0x1000)) + | usb_pipeslow (pipe) << 13 + | usb_maxpacket (usb_dev, pipe, usb_pipeout (pipe)) << 16); + +// debug("ep_add_ed: pipe=%x ed_num=%d ed->dma=%x ed->hwInfo=%x ed->hwHeadP=%x ed->hwTailP=%x\n", pipe, i, ed->dma, ed->hwINFO, ed->hwHeadP, ed->hwTailP); + + return ed; +} + +/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */ + +void +td_fill (ohci_t * ohci, unsigned int info, + void *data, int len, + struct urb * urb, int index) // *data should dma address of buffer +{ + ohci_td_t * td, * td_pt; + urb_priv_t * urb_priv = urb->hcpriv; + + if (index >= urb_priv->length) { + printf("internal OHCI error: TD index > length"); + return; + } + + /* use this td as the next dummy */ + td_pt = urb_priv->td [index]; + td_pt->hwNextTD = 0; + + /* fill the old dummy TD */ + td = urb_priv->td [index] = dma_to_td (ohci, + (void *)(le32_to_cpup (&urb_priv->ed->hwTailP) & ~0xf)); + +// debug("td_fill 2 td = %x, dma=%x , ed->hwHeadP=%x, ed->hwTailP=%x \n", td, td->td_dma, urb_priv->ed->hwHeadP, urb_priv->ed->hwTailP ); + + td->ed = urb_priv->ed; + td->next_dl_td = NULL; + td->index = index; + td->urb = urb; + td->data_dma = data; + if (!len) + data = 0; + + + td->hwINFO = cpu_to_le32 (info); + td->hwCBP = cpu_to_le32 ((uint32_t)data); + if (data) + td->hwBE = cpu_to_le32 ((uint32_t)data + len - 1); + else + td->hwBE = 0; + td->hwNextTD = cpu_to_le32 ((uint32_t)td_pt->td_dma); + + /* append to queue */ + td->ed->hwTailP = td->hwNextTD; + // debug("td_fill 4 td->td_dma=%x, td->hwINFO=%x\n", td->td_dma, td->hwINFO ); + // debug("td_fill 5 ed->dma=%x, ed->hwHeadP=%x, ed->hwTailP=%x \n", urb_priv->ed->dma, urb_priv->ed->hwHeadP, urb_priv->ed->hwTailP ); +} + +/* prepare all TDs of a transfer */ + +void td_submit_urb (struct urb * urb) +{ + urb_priv_t * urb_priv = urb->hcpriv; + ohci_t * ohci = (ohci_t *) &_ohci_x[urb->dev->controller]; + void * data; + int data_len = urb->transfer_buffer_length; + int cnt = 0; + u32 info = 0; + unsigned int toggle = 0; + void *setup_buffer; + + /* OHCI handles the DATA-toggles itself, we just use the USB-toggle bits for reseting */ + if(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe))) { + toggle = TD_T_TOGGLE; + } else { + toggle = TD_T_DATA0; + usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), 1); + } + + urb_priv->td_cnt = 0; + + if (data_len) { + data = (void *)virt_to_phys(urb->transfer_buffer); + } else + data = 0; + switch (usb_pipetype (urb->pipe)) { + case PIPE_BULK: + info = usb_pipeout (urb->pipe)? + TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ; + while(data_len > 4096) { + td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, 4096, urb, cnt); + data += 4096; data_len -= 4096; cnt++; + } + info = usb_pipeout (urb->pipe)? + TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ; + td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, data_len, urb, cnt); + cnt++; +#if 0 + /* If the transfer size is multiple of the pipe mtu, + * we may need an extra TD to create a empty frame + * Note : another way to check this condition is + * to test if(urb_priv->length > cnt) - Jean II */ + if ((urb->transfer_flags & USB_ZERO_PACKET) && + usb_pipeout (urb->pipe) && + (urb->transfer_buffer_length != 0) && + ((urb->transfer_buffer_length % maxps) == 0)) { + td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), 0, 0, urb, cnt); + cnt++; + } +#endif + +// debug("td_submit_urb 2 -- set OHCI_BLF\n"); + writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ + (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ + break; + + + case PIPE_CONTROL: + info = TD_CC | TD_DP_SETUP | TD_T_DATA0; + setup_buffer = (void *)virt_to_phys(urb->setup_packet); +// debug("td_sumbit_urb 11 setup_buffer = %x\n", setup_buffer); + td_fill (ohci, info, setup_buffer , 8, urb, cnt++); + if (data_len > 0) { + info = usb_pipeout (urb->pipe)? + TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1; + /* NOTE: mishandles transfers >8K, some >4K */ + td_fill (ohci, info, data, data_len, urb, cnt++); + } + info = usb_pipeout (urb->pipe)? + TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1; + td_fill (ohci, info, data, 0, urb, cnt++); +// debug("td_sumbit_urb 11 data = %x\n", data); + +// debug("td_submit_urb 2 -- set OHCI_CLF\n"); + writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ + (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ + break; + + } + if (urb_priv->length != cnt) { + debug("TD LENGTH %d != CNT %d", urb_priv->length, cnt); + } +} + +/* free HCD-private data associated with this URB */ + +void urb_free_priv (struct ohci *hc, urb_priv_t * urb_priv) +{ + int i; + int last = urb_priv->length - 1; + int len; + struct ohci_td *td; + + if (last >= 0) { +#if 0 + /* ISOC, BULK, INTR data buffer starts at td 0 + * CTRL setup starts at td 0 */ + td = urb_priv->td [0]; + + len = td->urb->transfer_buffer_length; + + /* unmap CTRL URB setup */ + if (usb_pipecontrol (td->urb->pipe)) { +// it should be freed in usb_control_msg_x +// forget2((void *)phys_to_virt((uint32_t)td->data_dma)); // 8 bytes + + /* CTRL data buffer starts at td 1 if len > 0 */ + if (len && last > 0) + td = urb_priv->td [1]; + } + + /* unmap data buffer */ + if (len && td->data_dma) { +// Don't need +// forget2((void *)phys_to_virt((uint32_t)td->data_dma)); + } +#endif + + for (i = 0; i <= last; i++) { + td = urb_priv->td [i]; + if (td) + td_free (hc, td); + } + } +#if URB_PRE_ALLOCATE!=1 + forget2((void *)urb_priv); +#endif +} + +/* get a transfer request */ + +int ohci_submit_urb (struct urb * urb) +{ + ohci_t * ohci; + ed_t * ed; + urb_priv_t * urb_priv; + unsigned int pipe = urb->pipe; + int i, size = 0; + int mem_flags = 0; + + if (!urb->dev) + return -ENODEV; + + if (urb->hcpriv) /* urb already in use */ + return -EINVAL; + + + ohci = (ohci_t *) &_ohci_x[urb->dev->controller]; +// printf("ohci_submit_urb: urb->dev port=%x, controller = %d ohci=%x ohci->ed=%x ohci->hcca=%x\n", urb->dev->port,urb->dev->controller, ohci, ohci->ed, ohci->hcca); + +#if DEBUG_USB==1 +// urb_print (urb, "SUB", usb_pipein (pipe)); +#endif + + +#if 0 + /* handle a request to the virtual root hub */ + if (usb_pipedevice (pipe) == ohci->rh.devnum) + return rh_submit_urb (urb); + + /* when controller's hung, permit only roothub cleanup attempts + * such as powering down ports */ + if (ohci->disabled) { + usb_dec_dev_use (urb->dev); + return -ESHUTDOWN; + } +#endif + + /* every endpoint has a ed, locate and fill it */ + if (!(ed = ep_add_ed (urb->dev, pipe, urb->interval, 1, mem_flags))) { + return -ENOMEM; + } +// debug("ohci_submit_usb: ed->dma=%x\n", ed->dma); + + /* for the private part of the URB we need the number of TDs (size) */ + switch (usb_pipetype (pipe)) { + case PIPE_BULK: /* one TD for every 4096 Byte */ + size = (urb->transfer_buffer_length - 1) / 4096 + 1; +#if 0 + /* If the transfer size is multiple of the pipe mtu, + * we may need an extra TD to create a empty frame + * Jean II */ + if ((urb->transfer_flags & USB_ZERO_PACKET) && + usb_pipeout (pipe) && + (urb->transfer_buffer_length != 0) && + ((urb->transfer_buffer_length % maxps) == 0)) + size++; +#endif + break; + case PIPE_CONTROL: /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */ + size = (urb->transfer_buffer_length == 0)? 2: + (urb->transfer_buffer_length - 1) / 4096 + 3; + break; + } + + /* allocate the private part of the URB */ +#if URB_PRE_ALLOCATE!=1 + urb_priv = allot2 (sizeof (urb_priv_t) + size * sizeof (ohci_td_t *), 0xff); + if (urb_priv == 0) { + printf("ohci_submit_usb: urb_priv allocated no mem\n"); + return -ENOMEM; + } +#else + urb_priv = ohci->urb_priv; +#endif + memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (ohci_td_t *)); + + /* fill the private part of the URB */ + urb_priv->length = size; + urb_priv->ed = ed; + + /* allocate the TDs (updating hash chains) */ + for (i = 0; i < size; i++) { + urb_priv->td[i] = td_alloc (ohci, 0); + if (!urb_priv->td[i]) { + urb_priv->length = i; + urb_free_priv (ohci, urb_priv); + return -ENOMEM; + } + } + + if (ed->state == ED_NEW || (ed->state & ED_DEL)) { + urb_free_priv (ohci, urb_priv); + return -EINVAL; + } + + urb->actual_length = 0; + urb->hcpriv = urb_priv; + urb->status = USB_ST_URB_PENDING; + /* link the ed into a chain if is not already */ + if (ed->state != ED_OPER) { + ep_link (ohci, ed); + } + + /* fill the TDs and link it to the ed */ + td_submit_urb (urb); + +#if 0 + /* drive timeouts by SF (messy, but works) */ + writel (OHCI_INTR_SF, &ohci->regs->intrenable); + (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ +#endif + + return 0; +} +/* calculate the transfer length and update the urb */ + +void dl_transfer_length(ohci_td_t * td) +{ + u32 tdINFO, tdBE, tdCBP; + struct urb * urb = td->urb; + urb_priv_t * urb_priv = urb->hcpriv; + + tdINFO = le32_to_cpup (&td->hwINFO); + tdBE = le32_to_cpup (&td->hwBE); + tdCBP = le32_to_cpup (&td->hwCBP); + + + if (!(usb_pipetype (urb->pipe) == PIPE_CONTROL && + ((td->index == 0) || (td->index == urb_priv->length - 1)))) { + if (tdBE != 0) { + if (td->hwCBP == 0) + urb->actual_length += tdBE - (uint32_t)td->data_dma + 1; + else + urb->actual_length += tdCBP - (uint32_t)td->data_dma; + } + + } + +// debug("td->td_dma=%x, urb->actual_length=%d\n", td->td_dma, urb->actual_length); +} + +/*-------------------------------------------------------------------------*/ + +/* replies to the request have to be on a FIFO basis so + * we reverse the reversed done-list */ + +ohci_td_t * dl_reverse_done_list (ohci_t * ohci) +{ + u32 td_list_hc; + ohci_td_t * td_rev = NULL; + ohci_td_t * td_list = NULL; + urb_priv_t * urb_priv = NULL; + uint32_t value; + u32 td_list_hc2; + int timeout = 1000000; //1 second +// unsigned long flags; +// Here need to process across the frame tds + td_list_hc = le32_to_cpup (&ohci->hcca->done_head) & 0xfffffff0; + td_list_hc2 = readl(&ohci->regs->donehead); +// debug("ohci->hcca->done_head = %x ohci->hcca=%x ohci=%x ohci->regs->donehead=%x\n", td_list_hc, ohci->hcca, ohci, td_list_hc2); + + td_list = dma_to_td (ohci, (void *)td_list_hc); + urb_priv = (urb_priv_t *) td_list->urb->hcpriv; + + while(/*(td_list_hc2!=0) || */(td_list->index < urb_priv->length-1) && (timeout>0)) { // wait another update for donehead + // To handle 1. ohci->hcca->donehead !=0 and regs->donehead!=0 + // 2. ohci->hcca->donehead !=0 and regs->donehead ==0 but regs-->donehead will be filled + + ohci->hcca->done_head = 0; + + value = readl(&ohci->regs->intrstatus); + value &= readl(&ohci->regs->intrenable); + + // We need to clear that the bit, otherwise We will not get next return. + if(value & OHCI_INTR_WDH) { + writel(value, &ohci->regs->intrstatus); + (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ +// debug("OHCI_INTR_WDH cleared intrstatus=%x value=%x \n", readl(&ohci->regs->intrstatus), value); + } + while(timeout>0) { // wait for next DONEHEAD_WRITEBACK + value = readl(&ohci->regs->intrstatus); + if(!(value & OHCI_INTR_WDH)) { + udelay(1); + timeout--; + continue; + } else { + break; + } + } + + td_list_hc2 = le32_to_cpup (&ohci->hcca->done_head) & 0xfffffff0; + // merge td_list_hc the tail of td_list_hc2 + + if(td_list_hc2!=0) { + + while (td_list_hc2) { + td_list = dma_to_td (ohci, (void *)td_list_hc2); + td_list_hc2 = le32_to_cpup (&td_list->hwNextTD) & 0xfffffff0; + } + + td_list->hwNextTD = td_list_hc; + + td_list_hc = le32_to_cpup (&ohci->hcca->done_head) & 0xfffffff0; + + td_list = dma_to_td (ohci, (void *)td_list_hc); + } else { + printf("."); + } + + } + + ohci->hcca->done_head = 0; + + value = readl(&ohci->regs->intrstatus); +// debug("OHCI_INTR_WDH value=%x \n", value); + value &= readl(&ohci->regs->intrenable); + +// We need to clear that the bit, otherwise We will not get next return. +// if(value & OHCI_INTR_WDH) { + writel(value, &ohci->regs->intrstatus); + (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ +// debug("OHCI_INTR_WDH cleared intrstatus=%x value=%x \n", readl(&ohci->regs->intrstatus), value); +// } + +#if 0 + if (value & OHCI_INTR_SO) { + debug("USB Schedule overrun"); + writel (OHCI_INTR_SO, &ohci->regs->intrenable); + (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ + } +#endif + + + while (td_list_hc) { +// debug("td_list_hc = %x\n", td_list_hc); + td_list = dma_to_td (ohci, (void *)td_list_hc); + + if (TD_CC_GET (le32_to_cpup (&td_list->hwINFO))) { + urb_priv = (urb_priv_t *) td_list->urb->hcpriv; + debug(" USB-error/status: %x : %x\n", + TD_CC_GET (le32_to_cpup (&td_list->hwINFO)), td_list); + if (td_list->ed->hwHeadP & cpu_to_le32 (0x1)) { + if (urb_priv && ((td_list->index + 1) < urb_priv->length)) { + td_list->ed->hwHeadP = + (urb_priv->td[urb_priv->length - 1]->hwNextTD & cpu_to_le32 (0xfffffff0)) | + (td_list->ed->hwHeadP & cpu_to_le32 (0x2)); + urb_priv->td_cnt += urb_priv->length - td_list->index - 1; + } else + td_list->ed->hwHeadP &= cpu_to_le32 (0xfffffff2); + } + } + + td_list->next_dl_td = td_rev; + td_rev = td_list; + td_list_hc = le32_to_cpup (&td_list->hwNextTD) & 0xfffffff0; + } + return td_list; +} +/*-------------------------------------------------------------------------*/ +/* td done list */ + +void dl_done_list (ohci_t * ohci, ohci_td_t * td_list) +{ + ohci_td_t * td_list_next = NULL; + ed_t * ed; + // int cc = 0; + struct urb * urb; + urb_priv_t * urb_priv; + u32 tdINFO; //, edHeadP, edTailP; + +// unsigned long flags; + + while (td_list) { + td_list_next = td_list->next_dl_td; + + urb = td_list->urb; + urb_priv = urb->hcpriv; + tdINFO = le32_to_cpup (&td_list->hwINFO); + + ed = td_list->ed; + + dl_transfer_length(td_list); +#if 0 + /* error code of transfer */ + cc = TD_CC_GET (tdINFO); + if (cc == TD_CC_STALL) + usb_endpoint_halt(urb->dev, + usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe)); + + if (!(urb->transfer_flags & USB_DISABLE_SPD) + && (cc == TD_DATAUNDERRUN)) + cc = TD_CC_NOERROR; + + if (++(urb_priv->td_cnt) == urb_priv->length) { + if ((ed->state & (ED_OPER | ED_UNLINK)) + && (urb_priv->state != URB_DEL)) { + urb->status = cc_to_error[cc]; + ohci_return_urb (ohci, urb); + } + else { + dl_del_urb (urb); + } + } + + if (ed->state != ED_NEW) { + edHeadP = le32_to_cpup (&ed->hwHeadP) & 0xfffffff0; + edTailP = le32_to_cpup (&ed->hwTailP); + + /* unlink eds if they are not busy */ + if ((edHeadP == edTailP) && (ed->state == ED_OPER)) + ep_unlink (ohci, ed); + } +#endif + + td_list = td_list_next; + } +} + +void ohci_wait_urb_done(struct urb *urb, int timeout) { // timeout usually ==10000 --> 10milisecond + //here need to according the urb or ed type judge the BLF and CLF, We may need one time out in it + // Or need to check intrstatus and see if the hcca->done_head has been filled. + // We need to clear that the bit, otherwise We will get next return. + uint32_t pipe = urb->pipe; + uint32_t value; + usbdev_t *usb_dev = urb->dev; + ohci_t *ohci = &_ohci_x[usb_dev->controller]; + uint32_t type; + while(timeout>0) { +#if 1 + value = readl(&ohci->regs->intrstatus); + if(!(value & OHCI_INTR_WDH)) { + udelay(1); + timeout--; + continue; + } else { + break; + } +#endif + } +#if 1 + while (timeout>0) { + type = usb_pipetype (pipe); + if(type ==PIPE_BULK) { + if( (readl(&ohci->regs->cmdstatus) & OHCI_BLF) == 0) break; + } else if(type == PIPE_CONTROL) { + if( (readl(&ohci->regs->cmdstatus) & OHCI_CLF) == 0) break; + } + udelay(1); // + timeout--; + } +#endif + + +} +void ohci_urb_complete(struct urb *urb) { + + ohci_t *ohci = &_ohci_x[urb->dev->controller]; + // it will clear the done list. and urb's actual_length is updated + dl_done_list (ohci, dl_reverse_done_list (ohci)); + +#if DEBUG_USB==1 + urb_print (urb, "RET", usb_pipein (urb->pipe)); +#endif + + urb_free_priv(ohci, urb->hcpriv); // free the priv and td list + +} +/*-------------------------------------------------------------------*/ +// it will 1. call usb_bulk_msg_x +// 2. call dl_list and find the data return +int ohci_bulk_transfer( uchar devnum, uchar ep, unsigned int data_len, uchar *data) { + int actual_length; + uint32_t t = devnum; + uint32_t pipe = ((ep&0x80)? 0x80:0)|(t<<8)|(3<<30); + t = ep; + pipe |=(t&0xf)<<15; + + usb_bulk_msg_x(&usb_device[devnum], pipe, data, data_len, &actual_length, 10000, ohci_urb_complete); + + return actual_length; + + +} +// it will 1. Call usb_control_msg_x +// 2. call dl_done_list to get the data returned ----> should be packed in one usb_complete_t function +// and assigned that to urb + +int ohci_control_msg( uchar devnum, uchar request_type, uchar request, unsigned short wValue, unsigned short wIndex, unsigned short +wLength, void *data){ + + uint32_t t = devnum; + uint32_t pipe = ((request_type&0x80)? 0x80:0)|(t<<8)|(2<<30); + return usb_control_msg_x(&usb_device[devnum], pipe, request, request_type, wValue, wIndex, data, wLength, 10000, ohci_urb_complete); + +} + +int ohc_reset(uchar controller) +{ + + int timeout = 30; + int smm_timeout = 50; /* 0,5 sec */ + + debug("Resetting OHCI\n"); + ohci_regs = (ohci_regs_t *)hc_base[controller]; + ohci_t *ohci = &_ohci_x[controller]; + +#ifndef __hppa__ + /* PA-RISC doesn't have SMM, but PDC might leave IR set */ + if (readl (&ohci_regs->control) & OHCI_CTRL_IR) { /* SMM owns the HC */ + writel (OHCI_OCR, &ohci_regs->cmdstatus); /* request ownership */ + debug("USB HC TakeOver from SMM"); + while (readl (&ohci_regs->control) & OHCI_CTRL_IR) { + mdelay (10); + if (--smm_timeout == 0) { + printf("USB HC TakeOver failed!"); + return -1; + } + } + } +#endif + + debug("USB HC reset_hc usb-%08x: ctrl = 0x%x ;", + hc_base[controller], + readl (&ohci_regs->control)); + + /* Reset USB (needed by some controllers) */ + writel (0, &ohci_regs->control); + + /* Force a state change from USBRESET to USBOPERATIONAL for ALi */ + (void) readl (&ohci_regs->control); /* PCI posting */ + writel (ohci->hc_control = OHCI_USB_OPER, &ohci_regs->control); + + /* HC Reset requires max 10 ms delay */ + writel (OHCI_HCR, &ohci_regs->cmdstatus); + while ((readl (&ohci_regs->cmdstatus) & OHCI_HCR) != 0) { + if (--timeout == 0) { + printf("USB HC reset timed out!"); + return -1; + } + udelay (1); + } + return 0; +} + + +int ohc_start(uchar controller) { + // unsigned short tmp; + u32 mask; + unsigned int fminterval; + int delaytime; + ohci_regs = (ohci_regs_t *)hc_base[controller]; + ohci_t *ohci = &_ohci_x[controller]; + + debug("Starting OHCI\n"); + + writel (0, &ohci_regs->ed_controlhead); + writel (0, &ohci_regs->ed_bulkhead); + + writel ((uint32_t)ohci->hcca_dma, &ohci_regs->hcca); /* a reset clears this */ //3 + + fminterval = 0x2edf; //6 + writel ((fminterval * 9) / 10, &ohci_regs->periodicstart); // Don't worry, we can disable periodic in contol or let the ED list null + fminterval |= ((((fminterval - 210) * 6) / 7) << 16); + writel (fminterval, &ohci_regs->fminterval); + writel (0x628, &ohci_regs->lsthresh); + + /* start controller operations */ + + ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; + writel (ohci->hc_control, &ohci_regs->control); // PIE and IE is disabled + // DO we need to enable that but leave all ISO ED and INT ED list null??? + + mask = OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO; + writel (mask, &ohci->regs->intrenable); + writel (mask, &ohci->regs->intrstatus); + + /* required for AMD-756 and some Mac platforms */ + writel ((roothub_a (ohci) | RH_A_NPS) & ~RH_A_PSM, &ohci->regs->roothub.a); + writel (RH_HS_LPSC, &ohci->regs->roothub.status); + + (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ + + // POTPGT delay is bits 24-31, in 2 ms units. + delaytime = ((roothub_a (ohci) >> 23) & 0x1fe)*5/2; // for apacer 256 usb 2.0 + NEC 2.0 chip +// delaytime = ((roothub_a (ohci) >> 23) & 0x1fe); + + mdelay (delaytime); + +// printf("delaytime: %d\n", delaytime); + + return(0); +} + + +int ohc_init(struct pci_device *dev) +{ + uint16_t word; + uint32_t dword; + ohci_t *ohci; + ed_t * ed; + int i,j, NDP; + int size; + + pci_read_config_dword(dev, 0x10, &dword); // it will be 4k range + hc_base[num_controllers] = (uint32_t)phys_to_virt(dword); + ohci = &_ohci_x[num_controllers]; + debug("ohc_init num_controllers=%d ohci=%x\n", num_controllers, (uint32_t)ohci); + memset(ohci, 0, sizeof(ohci_t)); + ohci->regs = (ohci_regs_t *)hc_base[num_controllers]; + ohci_regs = ohci->regs; + + ohci->hcca = allot2(sizeof (struct ohci_hcca), 0xff); //1 + if (!ohci->hcca) { + printf("ohc_init: hcca allocated no MEM\n"); + return -ENOMEM; + } + memset (ohci->hcca, 0, sizeof (struct ohci_hcca)); + ohci->hcca_dma = (void *)virt_to_phys(ohci->hcca); + + //init ed; + ohci->ed = allot2(sizeof(ed_t)*NUM_EDS,0xf); + if(ohci->ed==0) { + printf("ohci_init: ed allocate no MEM\n"); + } +// debug("ohci->ed = %x\n", ohci->ed); + for(i=0; ied[i]; + ed->dma = (void *)virt_to_phys(ed); +// debug("i=%d, ed dma = %x\n", i, (uint32_t)ed->dma); + ed->state = ED_NEW; + } + +// init urb and urb_priv + ohci->urb = (struct urb *)allot2(sizeof(struct urb),0xff); + if (!ohci->urb) { + printf("ohci_init: urb allocate failed"); + } + memset(ohci->urb, 0, sizeof(urb_t)); + + + /* allocate the private part of the URB */ + size = 4; + ohci->urb_priv = allot2 (sizeof (urb_priv_t) + size * sizeof (ohci_td_t *), 0xff); + if (ohci->urb_priv == 0) { + printf("ohci_init: urb_priv allocated no mem\n"); + } + memset (ohci->urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (ohci_td_t *)); + + // set master + pci_read_config_word(dev, 0x04, &word); + word |= 0x04; + pci_write_config_word(dev, 0x04, word); + + + DPRINTF("Found OHCI at %08x\n", hc_base[num_controllers]); + ohc_reset(num_controllers); + + /* Here should or move to ohc_start + 1. Init HCCA + 2. Init ED and TD ---> in submit_urb + 3. Assign HCCA to ohci_regs->hcca ---> in ohc_init + 4. Set Intr to ohci_regs->intrenable ---> disable that in ohc_init + 5. enable all queue in ohci_regs->control ---> in ep_link and it is called by submit_urb + 6. set peridicstart to 0.9 of frameinterval ---> ohc_start + */ + +// writel( 0, &ohci_regs->intrenable); // no interrupts! //4 +// writel( 0xffffffff, &ohci_regs->intrdisable); + + NDP = readl(&ohci->regs->roothub.a) & 0xff; + for(j=0;jregs->roothub.portstatus[j]); + } + + /* FIXME this is a second HC reset; why?? */ + writel (ohci->hc_control = OHCI_USB_RESET, &ohci->regs->control); + (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ + mdelay (10); + + ohc_start(num_controllers); + + num_controllers++; + +#if DEBUG_USB==1 +// ohci_dump (ohci, 1); +#endif + +// debug("ohci->ed = %x\n", ohci->ed); + return(0); +} +int poll_o_root_hub(uint32_t port, uchar controller) +{ + uint32_t value; + int addr=0; + int i; + static uint32_t do_over=0; + uint8_t what; + ohci_t *ohci; + + value = readl(port); + + debug("poll_o_root_hub1 v=%08x port = %x, controller = %d\n", value, port, controller); + + if(value == 0xffffffff) return addr; // stupid port + + if((value & RH_PS_CSC) || do_over == port) { + debug("poll_o_root_hub2 v=%08x\t", value); + do_over=0; + if(value & RH_PS_CCS ) { // if port connected + debug("poll_o_root_hub21 v=%08x\t", value); + DPRINTF("Connection on port %04x\n", port); + + writel(value, port); + for(i=0; i<40; i++) { + udelay(10000+usec_offset); + value = readl(port); + if(value & RH_PS_CSC) { + writel(value, port); //Clear Change bit + i=0; + DPRINTF("BOUNCE!\n"); + } + } +// debug("poll_o_root_hub211 v=%08x\t", value); + + oport_wakeup(port); +// DPRINTF("Wakup %04x\n", port); + +// debug("poll_o_root_hub212 v=%08x\t", readl(port)); + oport_reset(port); +// debug("poll_o_root_hub213 v=%08x\t", readl(port)); + mdelay(10); + oport_enable(port); +// debug("poll_o_root_hub214 v=%08x\t", readl(port)); + + if(!(value & RH_PS_CCS)) { + DPRINTF("Device went away!\n"); + return(-1); + } + + addr = configure_device( port, controller, value & RH_PS_LSDA); + +#if DEBUG_USB==1 + // some one clear enable bit??? why??? It costs me one week to find it out. + ohci = &_ohci_x[controller]; + ohci_dump (ohci, 1); +#endif + +#if 1 +// usb_control_msg(addr, 0x21, 0xff, 0, 0, 0, NULL);// reset device +// mdelay(10); + usb_control_msg(addr, 0xa1, 0xfe, 0, 0, 1, &what); // get MAX L // get MAX LUN +#endif + +// debug("poll_o_root_hub215 v=%08x addr = %d\n", readl(port), addr); + + if(addr<0) { + oport_disable(port); + udelay(20000); +// oport_reset(port); + oport_reset_long(port); + oport_suspend(port); + do_over=port; + ohc_clear_stat(controller); + + } + } else { +// debug("poll_o_root_hub22 v=%08x\t", readl(port)); + oport_suspend(port); + oport_disable(port); + DPRINTF("Port %04x disconnected\n", port); + // wave hands, deconfigure devices on this port! + } + } + + return(addr); +} + + + + +#endif diff --git a/src/filo/usb/ohci.h b/src/filo/usb/ohci.h new file mode 100644 index 00000000..0c9c6ad5 --- /dev/null +++ b/src/filo/usb/ohci.h @@ -0,0 +1,316 @@ +#ifdef USB_DISK + +#ifndef _OHCI_H +#define _OHCI_H + +/******************************************************************************* + * + * + * Copyright 2003 Steven James and + * LinuxLabs http://www.linuxlabs.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. + * + ******************************************************************************/ + +// for OHCI + +/* ED States */ + +#define ED_NEW 0x00 +#define ED_UNLINK 0x01 +#define ED_OPER 0x02 +#define ED_DEL 0x04 +#define ED_URB_DEL 0x08 + +/* usb_ohci_ed */ +struct ed { + u32 hwINFO; + u32 hwTailP; + u32 hwHeadP; + u32 hwNextED; + + struct ed * ed_prev; + u8 int_period; // No use just for aligned + u8 int_branch; // No use just for aligned + u8 int_load; // No uae just for aligned + u8 int_interval; // No use just for aligned + u8 state; + u8 type; + u16 last_iso; // no use just for aligned + struct ed * ed_rm_list; // No use just for aligned + + void * dma; + + u32 unused[3]; +}; +// __attribute((aligned(16))); +typedef struct ed ed_t; + +/* TD info field */ +#define TD_CC 0xf0000000 +#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f) +#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28) +#define TD_EC 0x0C000000 +#define TD_T 0x03000000 +#define TD_T_DATA0 0x02000000 +#define TD_T_DATA1 0x03000000 +#define TD_T_TOGGLE 0x00000000 +#define TD_R 0x00040000 +#define TD_DI 0x00E00000 +#define TD_DI_SET(X) (((X) & 0x07)<< 21) +#define TD_DP 0x00180000 +#define TD_DP_SETUP 0x00000000 +#define TD_DP_IN 0x00100000 +#define TD_DP_OUT 0x00080000 + +#define TD_ISO 0x00010000 +#define TD_DEL 0x00020000 + +/* CC Codes */ +#define TD_CC_NOERROR 0x00 +#define TD_CC_CRC 0x01 +#define TD_CC_BITSTUFFING 0x02 +#define TD_CC_DATATOGGLEM 0x03 +#define TD_CC_STALL 0x04 +#define TD_DEVNOTRESP 0x05 +#define TD_PIDCHECKFAIL 0x06 +#define TD_UNEXPECTEDPID 0x07 +#define TD_DATAOVERRUN 0x08 +#define TD_DATAUNDERRUN 0x09 +#define TD_BUFFEROVERRUN 0x0C +#define TD_BUFFERUNDERRUN 0x0D +#define TD_NOTACCESSED 0x0F + + +#define MAXPSW 1 + +struct ohci_td { + u32 hwINFO; + u32 hwCBP; /* Current Buffer Pointer */ + u32 hwNextTD; /* Next TD Pointer */ + u32 hwBE; /* Memory Buffer End Pointer */ + u16 hwPSW[MAXPSW]; + u8 unused; + u8 index; + struct ed * ed; + struct ohci_td * next_dl_td; + struct urb * urb; //defined in usb.h + void * td_dma; + void * data_dma; + u32 unused2[2]; +}; +//__attribute((aligned(32))); /* normally 16, iso needs 32 */ +typedef struct ohci_td ohci_td_t; + +#define OHCI_ED_SKIP (1 << 14) + +/* + * The HCCA (Host Controller Communications Area) is a 256 byte + * structure defined in the OHCI spec. that the host controller is + * told the base address of. It must be 256-byte aligned. + */ + +#define NUM_INTS 32 /* part of the OHCI standard */ +struct ohci_hcca { + u32 int_table[NUM_INTS]; /* Interrupt ED table */ + u16 frame_no; /* current frame number */ + u16 pad1; /* set to 0 on each frame_no change */ + u32 done_head; /* info returned for an interrupt */ + u8 reserved_for_hc[116]; +} __attribute((aligned(256))); + + +#define MAX_ROOT_PORTS 15 + +struct ohci_regs { + /* control and status registers */ + u32 revision; + u32 control; + u32 cmdstatus; + u32 intrstatus; + u32 intrenable; + u32 intrdisable; + /* memory pointers */ + u32 hcca; + u32 ed_periodcurrent; + u32 ed_controlhead; + u32 ed_controlcurrent; + u32 ed_bulkhead; + u32 ed_bulkcurrent; + u32 donehead; + /* frame counters */ + u32 fminterval; + u32 fmremaining; + u32 fmnumber; + u32 periodicstart; + u32 lsthresh; + /* Root hub ports */ + struct ohci_roothub_regs { + u32 a; + u32 b; + u32 status; + u32 portstatus[MAX_ROOT_PORTS]; + } roothub; +} __attribute((aligned(32))); +typedef struct ohci_regs ohci_regs_t; + + +/* OHCI CONTROL AND STATUS REGISTER MASKS */ + +/* + * HcControl (control) register masks + */ +#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */ +#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */ +#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */ +#define OHCI_CTRL_CLE (1 << 4) /* control list enable */ +#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */ +#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */ +#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */ +#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */ +#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */ + +/* pre-shifted values for HCFS */ +# define OHCI_USB_RESET (0 << 6) +# define OHCI_USB_RESUME (1 << 6) +# define OHCI_USB_OPER (2 << 6) +# define OHCI_USB_SUSPEND (3 << 6) + +/* + * HcCommandStatus (cmdstatus) register masks + */ +#define OHCI_HCR (1 << 0) /* host controller reset */ +#define OHCI_CLF (1 << 1) /* control list filled */ +#define OHCI_BLF (1 << 2) /* bulk list filled */ +#define OHCI_OCR (1 << 3) /* ownership change request */ +#define OHCI_SOC (3 << 16) /* scheduling overrun count */ + +/* + * masks used with interrupt registers: + * HcInterruptStatus (intrstatus) + * HcInterruptEnable (intrenable) + * HcInterruptDisable (intrdisable) + */ +#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */ +#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */ +#define OHCI_INTR_SF (1 << 2) /* start frame */ +#define OHCI_INTR_RD (1 << 3) /* resume detect */ +#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */ +#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */ +#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */ +#define OHCI_INTR_OC (1 << 30) /* ownership change */ +#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */ + + +/* For initializing controller (mask in an HCFS mode too) */ +#define OHCI_CONTROL_INIT \ + (OHCI_CTRL_CBSR & 0x3) +//| OHCI_CTRL_IE | OHCI_CTRL_PLE + +/* OHCI ROOT HUB REGISTER MASKS */ + +/* roothub.portstatus [i] bits */ +#define RH_PS_CCS 0x00000001 /* current connect status */ +#define RH_PS_PES 0x00000002 /* port enable status*/ +#define RH_PS_PSS 0x00000004 /* port suspend status */ +#define RH_PS_POCI 0x00000008 /* port over current indicator */ +#define RH_PS_PRS 0x00000010 /* port reset status */ +#define RH_PS_PPS 0x00000100 /* port power status */ +#define RH_PS_LSDA 0x00000200 /* low speed device attached */ +#define RH_PS_CSC 0x00010000 /* connect status change */ +#define RH_PS_PESC 0x00020000 /* port enable status change */ +#define RH_PS_PSSC 0x00040000 /* port suspend status change */ +#define RH_PS_OCIC 0x00080000 /* over current indicator change */ +#define RH_PS_PRSC 0x00100000 /* port reset status change */ + +/* roothub.status bits */ +#define RH_HS_LPS 0x00000001 /* local power status */ +#define RH_HS_OCI 0x00000002 /* over current indicator */ +#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */ +#define RH_HS_LPSC 0x00010000 /* local power status change */ +#define RH_HS_OCIC 0x00020000 /* over current indicator change */ +#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */ + +/* roothub.b masks */ +#define RH_B_DR 0x0000ffff /* device removable flags */ +#define RH_B_PPCM 0xffff0000 /* port power control mask */ + +/* roothub.a masks */ +#define RH_A_NDP (0xff << 0) /* number of downstream ports */ +#define RH_A_PSM (1 << 8) /* power switching mode */ +#define RH_A_NPS (1 << 9) /* no power switching */ +#define RH_A_DT (1 << 10) /* device type (mbz) */ +#define RH_A_OCPM (1 << 11) /* over current protection mode */ +#define RH_A_NOCP (1 << 12) /* no over current protection */ +#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ + +typedef struct +{ + ed_t * ed; + u16 length; // number of tds associated with this request + u16 td_cnt; // number of tds already serviced + int state; +#if 0 + wait_queue_head_t * wait; +#endif + ohci_td_t * td[0]; // list pointer to all corresponding TDs associated with this request + +} urb_priv_t; + +#define NUM_EDS 32 /* num of preallocated endpoint descriptors */ + +typedef struct ohci { + struct ohci_hcca *hcca; /* hcca */ + void * hcca_dma; + + ohci_regs_t * regs; /* OHCI controller's memory */ + + ed_t * ed_bulktail; /* last endpoint of bulk list */ + ed_t * ed_controltail; /* last endpoint of control list */ + + int intrstatus; + u32 hc_control; /* copy of the hc control reg */ + + uint32_t ed_cnt; + ed_t *ed; // Allocate that from ed_buffer in ohc_init + usbdev_t *dev[NUM_EDS]; + urb_t *urb; // one ohci one urb + urb_priv_t *urb_priv; + struct usb_ctrlrequest *dr; +} ohci_t; + + +extern ohci_t _ohci_x[MAX_CONTROLLERS]; + +#define usb_to_ohci(usb_dev) (&_ohci_x[(usb_dev)->controller]) + +extern ohci_regs_t *ohci_regs; + +void clear_oport_stat(uint32_t port); +int ohc_init(struct pci_device *dev); +int poll_o_root_hub(uint32_t port, uchar controller); + +int ohci_bulk_transfer( uchar devnum, uchar ep, unsigned int data_len, uchar *data); +int ohci_control_msg( uchar devnum, uchar request_type, uchar request, unsigned short wValue, unsigned short wIndex, unsigned short + wLength, void *data); +void ohci_wait_urb_done(struct urb *urb, int timeout); + +void ohci_init(void); +int ohc_init(struct pci_device *dev); +int ohci_submit_urb (struct urb * urb); +#endif + +#endif diff --git a/src/filo/usb/scsi.h b/src/filo/usb/scsi.h new file mode 100644 index 00000000..7c119a48 --- /dev/null +++ b/src/filo/usb/scsi.h @@ -0,0 +1,226 @@ +/* Copyright (C) 1998, 1999 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C 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. + + The GNU C 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 the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* + * This header file contains public constants and structures used by + * the scsi code for linux. + */ + +#ifndef _SCSI_SCSI_H +#define _SCSI_SCSI_H 1 + +//#include + +/* + * SCSI opcodes + */ + +#define TEST_UNIT_READY 0x00 +#define REZERO_UNIT 0x01 +#define REQUEST_SENSE 0x03 +#define FORMAT_UNIT 0x04 +#define READ_BLOCK_LIMITS 0x05 +#define REASSIGN_BLOCKS 0x07 +#define READ_6 0x08 +#define WRITE_6 0x0a +#define SEEK_6 0x0b +#define READ_REVERSE 0x0f +#define WRITE_FILEMARKS 0x10 +#define SPACE 0x11 +#define INQUIRY 0x12 +#define RECOVER_BUFFERED_DATA 0x14 +#define MODE_SELECT 0x15 +#define RESERVE 0x16 +#define RELEASE 0x17 +#define COPY 0x18 +#define ERASE 0x19 +#define MODE_SENSE 0x1a +#define START_STOP 0x1b +#define RECEIVE_DIAGNOSTIC 0x1c +#define SEND_DIAGNOSTIC 0x1d +#define ALLOW_MEDIUM_REMOVAL 0x1e + +#define SET_WINDOW 0x24 +#define READ_CAPACITY 0x25 +#define READ_10 0x28 +#define WRITE_10 0x2a +#define SEEK_10 0x2b +#define WRITE_VERIFY 0x2e +#define VERIFY 0x2f +#define SEARCH_HIGH 0x30 +#define SEARCH_EQUAL 0x31 +#define SEARCH_LOW 0x32 +#define SET_LIMITS 0x33 +#define PRE_FETCH 0x34 +#define READ_POSITION 0x34 +#define SYNCHRONIZE_CACHE 0x35 +#define LOCK_UNLOCK_CACHE 0x36 +#define READ_DEFECT_DATA 0x37 +#define MEDIUM_SCAN 0x38 +#define COMPARE 0x39 +#define COPY_VERIFY 0x3a +#define WRITE_BUFFER 0x3b +#define READ_BUFFER 0x3c +#define UPDATE_BLOCK 0x3d +#define READ_LONG 0x3e +#define WRITE_LONG 0x3f +#define CHANGE_DEFINITION 0x40 +#define WRITE_SAME 0x41 +#define READ_TOC 0x43 +#define LOG_SELECT 0x4c +#define LOG_SENSE 0x4d +#define MODE_SELECT_10 0x55 +#define RESERVE_10 0x56 +#define RELEASE_10 0x57 +#define MODE_SENSE_10 0x5a +#define PERSISTENT_RESERVE_IN 0x5e +#define PERSISTENT_RESERVE_OUT 0x5f +#define MOVE_MEDIUM 0xa5 +#define READ_12 0xa8 +#define WRITE_12 0xaa +#define WRITE_VERIFY_12 0xae +#define SEARCH_HIGH_12 0xb0 +#define SEARCH_EQUAL_12 0xb1 +#define SEARCH_LOW_12 0xb2 +#define READ_ELEMENT_STATUS 0xb8 +#define SEND_VOLUME_TAG 0xb6 +#define WRITE_LONG_2 0xea + +/* + * Status codes + */ + +#define GOOD 0x00 +#define CHECK_CONDITION 0x01 +#define CONDITION_GOOD 0x02 +#define BUSY 0x04 +#define INTERMEDIATE_GOOD 0x08 +#define INTERMEDIATE_C_GOOD 0x0a +#define RESERVATION_CONFLICT 0x0c +#define COMMAND_TERMINATED 0x11 +#define QUEUE_FULL 0x14 + +#define STATUS_MASK 0x3e + +/* + * SENSE KEYS + */ + +#define NO_SENSE 0x00 +#define RECOVERED_ERROR 0x01 +#define NOT_READY 0x02 +#define MEDIUM_ERROR 0x03 +#define HARDWARE_ERROR 0x04 +#define ILLEGAL_REQUEST 0x05 +#define UNIT_ATTENTION 0x06 +#define DATA_PROTECT 0x07 +#define BLANK_CHECK 0x08 +#define COPY_ABORTED 0x0a +#define ABORTED_COMMAND 0x0b +#define VOLUME_OVERFLOW 0x0d +#define MISCOMPARE 0x0e + + +/* + * DEVICE TYPES + */ + +#define TYPE_DISK 0x00 +#define TYPE_TAPE 0x01 +#define TYPE_PROCESSOR 0x03 /* HP scanners use this */ +#define TYPE_WORM 0x04 /* Treated as ROM by our system */ +#define TYPE_ROM 0x05 +#define TYPE_SCANNER 0x06 +#define TYPE_MOD 0x07 /* Magneto-optical disk - + * - treated as TYPE_DISK */ +#define TYPE_MEDIUM_CHANGER 0x08 +#define TYPE_ENCLOSURE 0x0d /* Enclosure Services Device */ +#define TYPE_NO_LUN 0x7f + +/* + * standard mode-select header prepended to all mode-select commands + * + * moved here from cdrom.h -- kraxel + */ + +struct ccs_modesel_head + { + unsigned char _r1; /* reserved. */ + unsigned char medium; /* device-specific medium type. */ + unsigned char _r2; /* reserved. */ + unsigned char block_desc_length; /* block descriptor length. */ + unsigned char density; /* device-specific density code. */ + unsigned char number_blocks_hi; /* number of blocks in this block + desc. */ + unsigned char number_blocks_med; + unsigned char number_blocks_lo; + unsigned char _r3; + unsigned char block_length_hi; /* block length for blocks in this + desc. */ + unsigned char block_length_med; + unsigned char block_length_lo; + }; + +/* + * MESSAGE CODES + */ + +#define COMMAND_COMPLETE 0x00 +#define EXTENDED_MESSAGE 0x01 +#define EXTENDED_MODIFY_DATA_POINTER 0x00 +#define EXTENDED_SDTR 0x01 +#define EXTENDED_EXTENDED_IDENTIFY 0x02 /* SCSI-I only */ +#define EXTENDED_WDTR 0x03 +#define SAVE_POINTERS 0x02 +#define RESTORE_POINTERS 0x03 +#define DISCONNECT 0x04 +#define INITIATOR_ERROR 0x05 +#define ABORT 0x06 +#define MESSAGE_REJECT 0x07 +#define NOP 0x08 +#define MSG_PARITY_ERROR 0x09 +#define LINKED_CMD_COMPLETE 0x0a +#define LINKED_FLG_CMD_COMPLETE 0x0b +#define BUS_DEVICE_RESET 0x0c + +#define INITIATE_RECOVERY 0x0f /* SCSI-II only */ +#define RELEASE_RECOVERY 0x10 /* SCSI-II only */ + +#define SIMPLE_QUEUE_TAG 0x20 +#define HEAD_OF_QUEUE_TAG 0x21 +#define ORDERED_QUEUE_TAG 0x22 + +/* + * Here are some scsi specific ioctl commands which are sometimes useful. + */ +/* These are a few other constants only used by scsi devices. */ + +#define SCSI_IOCTL_GET_IDLUN 0x5382 + +/* Used to turn on and off tagged queuing for scsi devices. */ + +#define SCSI_IOCTL_TAGGED_ENABLE 0x5383 +#define SCSI_IOCTL_TAGGED_DISABLE 0x5384 + +/* Used to obtain the host number of a device. */ +#define SCSI_IOCTL_PROBE_HOST 0x5385 + +/* Used to get the bus number for a device. */ +#define SCSI_IOCTL_GET_BUS_NUMBER 0x5386 + +#endif /* scsi/scsi.h */ diff --git a/src/filo/usb/scsi_cmds.c b/src/filo/usb/scsi_cmds.c new file mode 100644 index 00000000..bd7b7fa8 --- /dev/null +++ b/src/filo/usb/scsi_cmds.c @@ -0,0 +1,512 @@ +#ifdef USB_DISK +/******************************************************************************* + * + * + * Copyright 2003 Steven James and + * LinuxLabs http://www.linuxlabs.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. + * + ******************************************************************************/ +#include +#include +#include +#include + +#define DEBUG_THIS DEBUG_USB +#include + +#define DPRINTF debug + + +#include "scsi.h" + + +#include "usb_scsi_low.h" + +#ifndef NULL +#define NULL (void *) 0x0 +#endif + +#include "scsi_cmds.h" + +devhandle sgh; + +typedef struct sense_data { + uchar code; + + uchar sense_key:4; + uchar res1:4; + + uchar additional_code; + uchar qualifier; + + uchar res2[3]; + + uchar length; +} __attribute__ ((packed)) sense_data_t; + +typedef struct fixed_sense_data { + uchar code:7; + uchar valid:1; + + uchar obs1; + + uchar sense_key:4; + uchar res1:1; + uchar ili:1; + uchar eom:1; + uchar mark:1; + + unsigned int info; + + uchar add_len; +} __attribute__ ((packed)) fixed_sense_data_t; + +typedef struct additional_fixed_data { + unsigned int info; + + uchar code; + uchar qualifier; + uchar fru; + + uchar specific[3]; +} __attribute__ ((packed)) additional_fixed_data_t; + + +void PrintSense(uchar *sense, int len) +{ + int i; + + DPRINTF( "sense data "); + for(i=0;i=0x72) { + sense_data_t *sd = (sense_data_t *) sense; + uchar *pos = sense+sizeof(sense_data_t); + uchar remaining = sd->length; + int dlen; + + DPRINTF("code = %02x, key = %1x, additional = %02x, qual = %02x\n", sd->code, sd->sense_key, sd->additional_code, sd->qualifier); + + while(remaining) { + DPRINTF("type = %02x", pos[0]); + dlen = pos[1]; + pos+=2; + remaining -=2; + + for(i=0; iadd_len; + additional_fixed_data_t *afd; + + + DPRINTF("code = %02x key = %1x\n", fd->code, fd->sense_key); + if(fd->mark) { + DPRINTF("filemark "); + } + + if(fd->eom) { + DPRINTF(" End Of Media "); + } + + if(fd->ili) { + DPRINTF("Illegal instruction"); + } + + DPRINTF("\n"); + + if(fd->valid) { + DPRINTF( "(valid) "); + } + + DPRINTF( "Info: %08x\n", ntohl(fd->info)); + + afd = (additional_fixed_data_t *) (sense + 8); + +// while(remaining) { + if(remaining) { + DPRINTF("command info = %08x\n", ntohl(afd->info)); + DPRINTF("code = %02x, qual = %02x, fru = %02x\n", afd->code, afd->qualifier, afd->fru); + DPRINTF("sense key data = %02x:%02x:%02x\n\n", afd->specific[2], afd->specific[1], afd->specific[0]); + + afd++; + remaining -= sizeof(additional_fixed_data_t); + } + } + +} + +typedef struct query_response { + uchar type:5; + uchar qualifier:3; + + uchar reserved1:7; + uchar removable:1; + + uchar version; + + uchar ResponseDataFormat:4; // should == 2 + uchar HiSup:1; // report luns cmd supported + uchar NormACA:1; + uchar obsolete:1; + uchar aerc:1; + + uchar AdditionalLength; // length of vendor specific data (beyond 96 bytes) + + uchar reserved2:7; + uchar sccs:1; // have raid controller + + uchar addr16:1; // + uchar obsolete2:2; + uchar MChnger:1; // media changer + uchar MultiP:1; // multi port + uchar vs:1; // ??? + uchar EncServ:1; // enclosure service + uchar BQue:1; // basic command queueing + + uchar vs2:1; + uchar CmdQue:1; // full command queueing + uchar obsolete4:1; + uchar linked:1; + uchar sync:1; + uchar wbus16:1; // + uchar obsolete3:1; + uchar RelAddr:1; // treletive addressing + + char vendor[8]; + char product[16]; + char revision[4]; + char vendor_data[20]; + + uchar ius:1; + uchar qas:1; + uchar clocking:2; // + uchar reserved3:4; + + unsigned short version_desc[8]; + + char reserved4[21]; +} query_response_t; + +typedef struct ReadBlockCMD { + uchar cmd; + + uchar reladdr:1; + uchar reserved:2; + uchar fua:1; // force unit access flush to media + uchar dpo:1; // direct page out, do not cache + uchar reserved2:3; + + unsigned int block_address; + uchar reserved3; + + unsigned short block_count; + + uchar control; +} __attribute__ ((packed)) ReadBlockCMD_t ; + +int ll_read_block(devhandle sgd, char *buffer, int blocknum, int count) +{ + int ret; + ReadBlockCMD_t rb; + char sensedat[32]; + + memset(&rb,0,sizeof(rb)); + rb.cmd = READ_10; + rb.block_address = htonl(blocknum); + rb.block_count = htons(count); + + ret = scsi_command( sgd, (uint8_t *)&rb, sizeof(rb), SG_DXFER_FROM_DEV, buffer, count * 512, sensedat, sizeof(sensedat)); + + if(ret<0) { + DPRINTF("ERROR: ll_read_block( %x, %x, %x, %x) = %d\n", sgd, buffer, blocknum, count, ret); + PrintSense(sensedat, 32); + } + + return(ret); + +} + +int ll_write_block(devhandle sgd, char *buffer, int blocknum, int count) +{ + int ret; + ReadBlockCMD_t rb; + char sensedat[32]; + + memset(&rb,0,sizeof(rb)); + rb.cmd = WRITE_10; + rb.block_address = htonl(blocknum); + rb.block_count = htons(count); + + ret = scsi_command( sgd, (uint8_t *)&rb, sizeof(rb), SG_DXFER_TO_DEV, buffer, count * 512, sensedat, sizeof(sensedat)); + + return(ret); +} + +typedef struct ReadLongCMD { + uchar cmd; + + uchar reladdr:1; + uchar correct:1; + uchar reserved:5; + + unsigned int block_address; + uchar reserved3; + + unsigned short length; + + uchar control; +} __attribute__ ((packed)) ReadLongCMD_t ; + +int ll_read_long(devhandle sgd, char *buffer, int blocknum, int size) +{ + int ret; + ReadLongCMD_t rb; + char sensedat[32]; + + memset(&rb,0,sizeof(rb)); + rb.cmd = READ_LONG; + rb.block_address = htonl(blocknum); + rb.length = htons(size); + + ret = scsi_command( sgd, (uint8_t *)&rb, sizeof(rb), SG_DXFER_FROM_DEV, buffer, size, sensedat, sizeof(sensedat)); + return(ret); +} + +unsigned char ReadCapacityCMD[10] = { READ_CAPACITY, 0, 0,0,0,0, 0,0,0, 0}; + +struct ReadCapacityResponse { + unsigned int block_address; + unsigned int block_length; +}; + +int get_capacity(devhandle sgd, unsigned long *block_count, unsigned int *blk_len) +{ + int ret; + struct ReadCapacityResponse response; + char sensedat[32]; + + ret = scsi_command(sgd, ReadCapacityCMD, sizeof(ReadCapacityCMD), SG_DXFER_FROM_DEV, (uint8_t *)&response, sizeof(response), sensedat, sizeof(sensedat) ); + if(ret<0) { + DPRINTF("ERROR:get capacity: %d\n", ret); + PrintSense(sensedat,32); + } + + + *block_count = ntohl(response.block_address) +1; + *blk_len = ntohl(response.block_length); + + return(ret); +} + +#define INQ_REP_LEN 96 +unsigned char InquiryCMD[6] = { INQUIRY, 0, 0, 0, INQ_REP_LEN, 0}; + +int query(devhandle sgd, query_response_t *qr) +{ + int ret; + char sensedat[32]; + + ret = scsi_command(sgd, InquiryCMD, sizeof(InquiryCMD), SG_DXFER_FROM_DEV, (uint8_t *)qr, sizeof(query_response_t), sensedat, sizeof(sensedat) ); + + if(ret<0){ + DPRINTF("query: IOCTL"); + } + + return(ret); +} + +typedef struct lun_list { + unsigned int list_length; + unsigned int reserved; + unsigned long long lun[16]; +} lun_list_t; + +#define REPORT_LUNS 0xa0 +unsigned char ReportLunsCMD[12] = { REPORT_LUNS, 0, 2, 0, 0, 0, 0, 0, 0, 128, 0, 0 }; + +int ReportLUNS(devhandle sgd, lun_list_t *list) +{ + int ret; + char sensedat[32]; + + memset (list, 0, sizeof(lun_list_t)); + ret = scsi_command(sgd, ReportLunsCMD, sizeof(ReportLunsCMD), SG_DXFER_FROM_DEV, (uint8_t *)list, sizeof(lun_list_t), sensedat, sizeof(sensedat) ); + + if(ret<0) { + DPRINTF("Report Luns: IOCTL"); + } + + list->list_length = ntohl(list->list_length); + + return(ret); +} + +typedef struct command_descriptor { + uchar opcode; + uchar reserved; + unsigned short service_action; + uchar reserved2; + + uchar action_valid:1; + uchar reserved3:7; + + unsigned short cdb_len; +} __attribute__ ((packed)) command_descriptor_t; + +typedef struct report_opcodes_result { + unsigned long length; + + command_descriptor_t command[256]; +} __attribute__ ((packed)) report_opcode_result_t; + + +#define REPORT_OPCODES 0xa3 + +typedef struct report_opcodes_cmd { + uchar cmd; + uchar reserved[5]; + unsigned int reply_len; + uchar reserved2; + uchar control; +} __attribute__ ((packed)) ReportOpcodesCMD_t; + +//ReportOpcodesCMD_t ReportOpcodesCMD = { cmd : REPORT_OPCODES, reply_len: htonl(sizeof(report_opcode_result_t)) }; + +int ReportOpCodes(devhandle sgd, report_opcode_result_t *list) +{ + int ret; + char sensedat[32]; + ReportOpcodesCMD_t ReportOpcodesCMD; + + memset (list, 0, sizeof(report_opcode_result_t)); + ReportOpcodesCMD.cmd = REPORT_OPCODES; + ReportOpcodesCMD.reply_len = htonl( sizeof(report_opcode_result_t)); + + ret = scsi_command(sgd, (uint8_t *)&ReportOpcodesCMD, sizeof(ReportOpcodesCMD_t), SG_DXFER_FROM_DEV, (uint8_t *)list, sizeof(report_opcode_result_t), sensedat, sizeof(sensedat) ); + + if(ret<0) { + DPRINTF("Report Luns: IOCTL"); + } + + list->length = ntohl(list->length); + + return(ret); +} + + +#define READ_ATTRIBUTE 0x8c +#define VOLUME_LIST 2 +#define PARTITION_LIST 3 + +typedef struct read_attribute_cmd { + uchar cmd; + + uchar action:5; + uchar res:3; + + uchar restricted[3]; + + uchar volume; + uchar res2; + uchar partition; + + ushort attribute; + unsigned int reply_len; + uchar res3; + uchar control; +} __attribute__ ((packed)) ReadAttributeCMD_t; + +int CheckVolumes(devhandle sgd) +{ + int ret; + uchar reply[4]; + uchar sensedat[32]; + ReadAttributeCMD_t cmd; + + memset(&cmd,0,sizeof(cmd)); + + cmd.cmd=READ_ATTRIBUTE; + cmd.action = VOLUME_LIST; + cmd.reply_len = htonl(4); + + ret = scsi_command(sgd, (uint8_t *)&cmd, sizeof(cmd), SG_DXFER_FROM_DEV, reply, sizeof(reply), sensedat, sizeof(sensedat) ); + if(ret<0) { + DPRINTF("Report Volumes: IOCTL"); + return(-1); + } + + if(! reply[0] && !reply[1]) + return(0); + + return(reply[3]); +} + +int CheckPartitions(devhandle sgd) +{ + int ret; + uchar reply[4]; + uchar sensedat[32]; + ReadAttributeCMD_t cmd; + + memset(&cmd,0,sizeof(cmd)); + + cmd.cmd=READ_ATTRIBUTE; + cmd.action = PARTITION_LIST; + cmd.reply_len = htonl(4); + + ret = scsi_command(sgd, (uint8_t *)&cmd, sizeof(cmd), SG_DXFER_FROM_DEV, reply, sizeof(reply), sensedat, sizeof(sensedat) ); + if(ret<0) { + DPRINTF("Report PARTITIONVolumes: IOCTL"); + return(-1); + } + + if(! reply[0] && !reply[1]) + return(0); + + return(reply[3]); +} + +int UnitReady(devhandle sgd) +{ + uchar cmd[6]; + uchar sensedat[32]; + int ret; + + memset(cmd,0,sizeof(cmd)); + + ret = scsi_command(sgd, &cmd, sizeof(cmd), SG_DXFER_FROM_DEV, NULL, 0, sensedat, sizeof(sensedat) ); + if(ret<0) { + DPRINTF("UnitReady :"); + return(0); + } + + return(1); +} + + +#endif diff --git a/src/filo/usb/scsi_cmds.h b/src/filo/usb/scsi_cmds.h new file mode 100644 index 00000000..1c64a3bf --- /dev/null +++ b/src/filo/usb/scsi_cmds.h @@ -0,0 +1,14 @@ +#ifndef _SCSI_CMDS_H +#define _SCSI_CMDS_H + +#define devhandle uint8_t + +#define uchar uint8_t +#define ushort uint16_t + +void PrintSense(uchar *sense, int len); +int ll_read_block(devhandle sgd, char *buffer, int blocknum, int count); + +int get_capacity(devhandle sgd, unsigned long *block_count, unsigned int *blk_len); +int UnitReady(uchar sgd); +#endif diff --git a/src/filo/usb/uhci.c b/src/filo/usb/uhci.c new file mode 100644 index 00000000..4ce4b499 --- /dev/null +++ b/src/filo/usb/uhci.c @@ -0,0 +1,1143 @@ +#ifdef USB_DISK + +/******************************************************************************* + * + * + * Copyright 2003 Steven James and + * LinuxLabs http://www.linuxlabs.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. + * + ******************************************************************************/ + +#include +#include +#include +#include + +#define DEBUG_THIS DEBUG_USB +#include + +#define DPRINTF debug + + +#include "usb.h" +#include "uhci.h" +#include "debug_x.h" + +#define ALLOCATE 1 + +extern int usec_offset; + +int wait_head( queue_head_t *head, int count) +{ + td_t *td; + + + while(!head->depth.terminate) { + td = MEM_ADDR(head->depth.link); + if(!td->active) + return(-1); // queue failed + + if(count) + if(! --count) + return(0); // still active + + udelay(500); // give it some time + } + + return(1); // success +} + +queue_head_t *free_qh; +queue_head_t _queue_heads[MAX_QUEUEHEAD]; +queue_head_t *queue_heads = _queue_heads; + +queue_head_t *new_queue_head(void) +{ + queue_head_t *qh; + + if(!free_qh) + return(NULL); + + qh = free_qh; + free_qh = MEM_ADDR(qh->bredth.link); + + memset(qh,0,sizeof(queue_head_t)); + qh->bredth.terminate = qh->depth.terminate=1; + + return(qh); +} + +void free_queue_head( queue_head_t *qh) +{ + + qh->bredth.link = LINK_ADDR(free_qh); + if(!free_qh) + qh->bredth.terminate=1; + + qh->depth.terminate=1; + free_qh = qh; +} + +void init_qh(void) +{ + int i; + + for(i=0; ilink.link); +// DPRINTF("new_td: free_td_list = %p\n", free_td_list); + + memset(td, 0, sizeof(td_t)); + td->link.terminate=1; + +// DPRINTF("new_td: returning %p\n", td); + return(td); +} + +td_t *find_last_td(td_t *td) +{ + td_t *last; + + last = td; + + while(!last->link.terminate) + last = MEM_ADDR(last->link.link); + + return(last); +} + +void free_td( td_t *td) +{ + td_t *last_td; + + last_td = find_last_td(td); + + last_td->link.link = LINK_ADDR(free_td_list); + if(!free_td_list) + last_td->link.terminate=1; + else + last_td->link.terminate=0; + + free_td_list = td; + +} + +link_pointer_t *queue_end( queue_head_t *queue) +{ + link_pointer_t *link; + + link = &(queue->depth); + + while(!link->terminate) + link = MEM_ADDR(link->link); + + return(link); +} + +void add_td( queue_head_t *head, td_t *td) +{ + link_pointer_t *link; + + link = queue_end(head); + + link->link = LINK_ADDR(td); + link->terminate=0; +} + +transaction_t transactions[MAX_TRANSACTIONS]; +transaction_t *free_transactions; + +void init_transactions(void) +{ + int i; + + memset(transactions, 0, sizeof(transactions)); + + for(i=0; itd_list ); + free_queue_head( my_current->qh ); + + last = my_current; + my_current = my_current->next; + } + + last->next = free_transactions; + free_transactions = trans; +} + +transaction_t *new_transaction(td_t *td) +{ + transaction_t *trans = free_transactions; + queue_head_t *qh; + + if(!trans) { + DPRINTF("new_transaction( td = %x) failed!\n", td); + return(NULL); + } + + free_transactions = trans->next; + + memset(trans, 0, sizeof(transaction_t)); + + if(td) { + qh = new_queue_head(); + if(!qh) { + free_transaction(trans); + return(NULL); + } + + trans->qh = qh; + trans->td_list = td; + qh->depth.link = LINK_ADDR(td); + qh->depth.terminate = 0; + qh->bredth.terminate=1; + } + + return(trans); +} + +transaction_t *add_transaction( transaction_t *trans, td_t *td) +{ + transaction_t *t1; + + + t1 = new_transaction(td); + if(!t1) + return(NULL); + + trans->next = t1; + trans->qh->bredth.terminate=0; + trans->qh->bredth.link = LINK_ADDR(t1->qh); + trans->qh->bredth.queue=1; + + return(trans); +} + +link_pointer_t *frame_list[MAX_CONTROLLERS]; +#if 0 +uchar fl_buffer[MAX_CONTROLLERS][8192]; +#endif + +void init_framelist(uchar dev) +{ + + int i; +#if 0 + DPRINTF("raw frame_list is at %x\n", fl_buffer[dev]); + frame_list[dev] = (link_pointer_t *) ((bus_to_virt)(((unsigned int)virt_to_bus(fl_buffer[dev]) & ~0xfff) + 0x1000)); +#else + frame_list[dev] = (link_pointer_t *) allot2(sizeof(link_pointer_t)*1024, 0xfff); // 4K alignment + if(frame_list[dev]==0) { + printf("init_framelist: no mem\n"); + } +#endif + memset(frame_list[dev], 0, 1024 * sizeof(link_pointer_t)); + + + DPRINTF("frame_list is at %x\n", frame_list[dev]); + + for(i=0;i<1024;i++) + frame_list[dev][i].terminate=1; + +} + + +extern int num_controllers; + +extern uint32_t hc_base[MAX_CONTROLLERS]; +extern uint8_t hc_type[MAX_CONTROLLERS]; + +void uhc_clear_stat() +{ + unsigned short value; + + value = inw(USBSTS(0)); + outw(value, USBSTS(0)); +} + +void clear_uport_stat(unsigned short port) +{ + unsigned short value; + + value = inw(port); + outw(value, port); +} + +void uport_suspend( unsigned short port) +{ + unsigned short value; + + value = inw(port); + value |= 0x1000; + outw( value, port); + +} + +void uport_wakeup( unsigned short port) +{ + unsigned short value; + + value = inw(port); + value &= ~0x1000; + outw( value, port); + +} + +#if 0 +void uport_resume( unsigned short port) +{ + unsigned short value; + + value = inw(port); + value |= 0x40; + outw(value, port); + udelay(20000+usec_offset); + value &= ~0x40; + outw(value, port); + + do { + value = inw(port); + } while(value & 0x40); +} + +#endif +void uport_enable( unsigned short port) +{ + unsigned short value; + + value = inw(port); + value |= 0x04; + outw( value, port); + + do { + value = inw(port); + } while( !(value & 0x04) && (value & 0x01)); + +} + + +void uport_disable( unsigned short port) +{ + unsigned short value; + + value = inw(port); + value &= ~0x04; + outw( value, port); +} + +void uport_reset(unsigned short port) +{ + unsigned short value; + int i; + + value = inw(port); + value |= 0x200; + + outw( value, port); + + for(i=0;i<5;i++) + udelay(10000+usec_offset); + + value &= ~0x200; + outw( value, port); + +// DPRINTF("Port %04x reset\n", port); +} + +void uport_reset_long(unsigned short port) +{ + unsigned short value; + int i; + + value = inw(port); + value |= 0x200; + outw( value, port); + + for(i=0; i<20; i++) + udelay(10000); + + value &= ~0x200; + outw( value, port); + +// DPRINTF("Port %04x reset\n", port); +} + +void uhc_reset(uchar controller) +{ + DPRINTF("Resetting UHCI\n"); + outw(0x04, USBCMD(controller)); + udelay(20000); + outw(0, USBCMD(controller)); +} +#if 0 +int uhc_stop(uchar dev) +{ + unsigned short tmp; + + tmp = inw(USBCMD(dev)); + tmp &= ~USBCMDRUN; + outw( tmp, USBCMD(dev)); + + while(! (inw(USBSTS(dev)) & USBSTSHALTED) ); + outw( USBSTSHALTED, USBSTS(dev)); // clear the status + + return(0); +} + +#endif + +int uhc_start(uchar dev) { + unsigned short tmp; + + DPRINTF("Starting UHCI\n"); + + tmp = inw(USBCMD(dev)); + tmp |= USBCMDRUN; + +// tmp |= USBCMD_DEBUG; + outw( tmp, USBCMD(dev)); + + return(0); +} + +int uhc_init(struct pci_device *dev) +{ + int16_t word; + + + pci_read_config_word(dev, 0x20, &word); + hc_base[num_controllers] = word; + hc_base[num_controllers] &= ~1; + + DPRINTF("Found UHCI at %04x\n", hc_base[num_controllers]); + uhc_reset(num_controllers); + + // set master + pci_read_config_word(dev, 0x04, &word); + word |= 0x04; + pci_write_config_word(dev, 0x04, word); + +#if 0 + if( ((unsigned int) virt_to_bus(frame_list[num_controllers])) != ( ( (unsigned int)virt_to_bus(frame_list[num_controllers])) & ~0x7ff) ) { + DPRINTF("UHCI: grave error, misaligned framelist (%x)\n", frame_list[num_controllers]); + return(-1); + } +#endif + + DPRINTF("uhc_init setting framelist to: %08x\n", (unsigned int) virt_to_bus( (frame_list[num_controllers]) )); + outl( (unsigned int) virt_to_bus(frame_list[num_controllers]), FLBASE(num_controllers)); + outw( 0, FRNUM(num_controllers)); + outw( 0, USBINTR(num_controllers)); // no interrupts! + + outw(0x1000, PORTSC1(num_controllers)); + outw(0x1000, PORTSC2(num_controllers)); + + uhc_start(num_controllers); + + dump_uhci(hc_base[num_controllers]); + + num_controllers++; + return(0); +} + +queue_head_t *sched_queue[MAX_CONTROLLERS]; +queue_head_t *term_qh[MAX_CONTROLLERS]; +//td_t *dummy_td[MAX_CONTROLLERS]; +td_t *loop_td[MAX_CONTROLLERS]; + +void init_sched(uchar dev) +{ + int i; + +// dummy_td[dev] = new_td(); + loop_td[dev] = new_td(); + term_qh[dev] = new_queue_head(); + + sched_queue[dev] = new_queue_head(); + sched_queue[dev]->bredth.terminate=0; + sched_queue[dev]->bredth.queue=1; + sched_queue[dev]->bredth.link=LINK_ADDR(term_qh[dev]); + sched_queue[dev]->depth.terminate=1; + + term_qh[dev]->bredth.terminate=1; + term_qh[dev]->depth.link = LINK_ADDR(loop_td[dev]); + term_qh[dev]->depth.terminate=0; + +// dummy_td->link.link = LINK_ADDR(sched_queue); +// dummy_td->link.queue = 1; +// dummy_td->link.depth=1; +// dummy_td->link.terminate=0; +// dummy_td->packet_type = IN_TOKEN; +// dummy_td->max_transfer = 0x7; +// dummy_td->isochronous=1; +// dummy_td->active=1; +// dummy_td->device_addr = 0x7f; +// dummy_td->endpoint=0x01; +// dummy_td->buffer = virt_to_bus(&dummy_td->data[2]); +// dummy_td->retrys=3; + +//dump_hex( (uchar *) dummy_td, sizeof(td_t), "dummy_td "); + + loop_td[dev]->link.link = LINK_ADDR(loop_td[dev]); + loop_td[dev]->link.terminate=0; + loop_td[dev]->link.queue=0; + loop_td[dev]->packet_type = IN_TOKEN; + loop_td[dev]->max_transfer=7; + loop_td[dev]->retrys=0; + loop_td[dev]->device_addr=0x7f; + + for(i=0; i< 1024; i++) { + frame_list[dev][i].link = LINK_ADDR(sched_queue[dev]); + frame_list[dev][i].queue=1; + frame_list[dev][i].terminate=0; +// frame_list[dev][i].terminate=1; + } + + dump_link( frame_list[dev], "frame_list_link: "); +// DPRINTF("dummy_td = %x\n", dummy_td[dev]); + +// dump_frame_list("sched:"); + +} + +void uhci_init(void) +{ + int i; + + init_td(); + init_qh(); + init_transactions(); + + for(i=0;idepth.terminate) + return(1); + + while(strikes--) { + if(qh->depth.terminate) + return(1); + + td = MEM_ADDR(qh->depth.link); + + if(td->active) + return(0); + + udelay(1000); + +// if(!td->active) +// return(1); + } + + return(1); +} + +int wait_queue_complete( queue_head_t *qh) +{ + int ret; + int spins=1000; + + while( --spins && !(ret = poll_queue_head(qh))) { + udelay(1500); +// if(!(spins%30)) +// DPRINTF("wait_queue_complete: spin\n"); + } +// DPRINTF("wait_queue_complete: returning %d\n", ret); + + if(!spins) + return(-1); + + return(ret); +} + +#define BULK_DEPTH 1 + +transaction_t *_bulk_transfer( uchar devnum, uchar ep, unsigned int len, uchar *data) +{ + uchar dt; + transaction_t *trans; + td_t *td, *cur, *last; + int remaining = len; + uchar *pos = data; + int max; + uchar type = OUT_TOKEN; + int packet_length; + + + if(ep & 0x80) + type = IN_TOKEN; + + ep &= 0x7f; + + td = cur = last = NULL; + dt = usb_device[devnum].toggle[ep]; + max = usb_device[devnum].max_packet[ep]; + + while(remaining) { + cur = new_td(); + cur->packet_type = type; + cur->data_toggle = dt; + cur->endpoint = ep&0x7f; + cur->device_addr = devnum; + cur->detect_short=1; + cur->active=1; + dt = dt^0x01; + + if(!td){ + td = cur; + } + + if(last) { + last->link.terminate=0; + last->link.link = LINK_ADDR(cur); + } + + cur->buffer = (void *) virt_to_bus(pos); + + if(remaining>max) { + packet_length = max; + } + else { + packet_length = remaining; + } + + cur->max_transfer=packet_length-1; + cur->link.depth = BULK_DEPTH; + + remaining -= packet_length; + pos+= packet_length; + last = cur; + } + +// if( packet_length == max) { // if final packet wasn't short, add a zero packet +// cur = new_td(); +// dt = dt^0x01; +// cur->packet_type = type; +// cur->max_transfer = 0x7ff; // zero length code +// last->link.terminate=0; +// last->link.link = LINK_ADDR(cur); +// +// } + + cur->link.terminate=1; + + trans = new_transaction(td); + usb_device[devnum].toggle[ep] = dt; + + return(trans); +} + +#define DEPTH 0 + +transaction_t *ctrl_msg(uchar devnum, uchar request_type, uchar request, unsigned short wValue, unsigned short wIndex, unsigned short wLength, uchar *data) +{ + td_t *td; + td_t *current_td; + td_t *last_td; + transaction_t *trans; + + ctrl_msg_t *message; + + unsigned char type; + int remaining = wLength; + uchar *pos = data; + uchar dt=1; + +// DPRINTF("ctrl_msg( %02x, %02x, %02x, %04x, %04x, %04x, %p)\n", devnum, request_type, request, wValue, wIndex, wLength, data); +// DPRINTF("%d bytes in payload\n", remaining); +// DPRINTF("lowspeed = %u\n", usb_device[devnum].lowspeed); + last_td = td = new_td(); + + td->packet_type = SETUP_TOKEN; + td->device_addr = devnum & 0x7f; + td->max_transfer = 7; // fixed for setup packets + td->retrys = CTRL_RETRIES; + td->active=1; + td->data_toggle=0; + td->link.depth=DEPTH; + td->detect_short=0; + td->interrupt=1; + td->lowspeed = usb_device[devnum].lowspeed; + +// steal 8 bytes from so-called software area to hole the control message itself + td->buffer = (void *) virt_to_bus(&(td->data[2])); + message = bus_to_virt( (unsigned int) td->buffer); + + message->bmRequestType = request_type; + message->bRequest = request; + message->wValue = wValue; + message->wIndex = wIndex; + message->wLength = wLength; +//dump_hex(td, sizeof(td_t), "ctrl_msg:"); + trans = new_transaction(td); + + if(!trans) { + DPRINTF("ctrl_msg: couldn't allocate a transaction!\n"); + return(NULL); + } + + if(request_type & CONTROL_DIR_MASK) + type = IN_TOKEN; + else + type = OUT_TOKEN; + + while(remaining >0) { + int length; + +// DPRINTF("ctrl_msg loop %d remaining, maxpacket = %u\n", remaining, usb_device[devnum].max_packet[0]); + current_td = new_td(); + + last_td->link.link = LINK_ADDR(current_td); + last_td->link.terminate=0; + last_td->link.queue=0; + last_td->link.depth=DEPTH; + + + current_td->device_addr = devnum & 0x7f; + current_td->retrys = CTRL_RETRIES; + current_td->active=1; + current_td->data_toggle=dt; + current_td->link.depth=DEPTH; + current_td->lowspeed = usb_device[devnum].lowspeed; + current_td->detect_short=1; + + dt = dt^0x01; + + current_td->packet_type = type; +// if(type == IN_TOKEN) +// current_td->detect_short=1; + + if(remaining >usb_device[devnum].max_packet[0]) + length = usb_device[devnum].max_packet[0]; + else + length = remaining; + + current_td->max_transfer = length-1; + current_td->buffer = (void *) virt_to_bus(pos); + remaining -= length; + pos += length; + + last_td = current_td; + } + + current_td = new_td(); + + current_td->device_addr = devnum & 0x7f; + current_td->retrys = CONTROL_STS_RETRIES; + current_td->active=1; + current_td->lowspeed = usb_device[devnum].lowspeed; + + if(type == IN_TOKEN) + current_td->packet_type = OUT_TOKEN; + else + current_td->packet_type = IN_TOKEN; + + current_td->max_transfer=0x7ff; + + current_td->link.terminate=1; + current_td->data_toggle=1; + current_td->link.depth=DEPTH; + + + last_td->link.link = LINK_ADDR(current_td); + last_td->link.terminate=0; + last_td->link.queue=0; + last_td->link.depth=DEPTH; + + return(trans); +} + + +int schedule_transaction( uchar dev, transaction_t *trans) +{ + unsigned short value; + + if(!sched_queue[dev]->depth.terminate) + return(-EBUSY); + + sched_queue[dev]->depth.link = LINK_ADDR(trans->qh); + sched_queue[dev]->depth.terminate = 0; + sched_queue[dev]->depth.queue=1; + + if(hc_type[dev]==0x00) { + value = inw(hc_base[dev]); + value |=1; + outw( value, hc_base[dev]); + } +#if 0 + else if (hc_type[dev]==0x10) { + uint32_t value; + ohci_regs_t *ohci_regs = (ohci_regs_t *) hc_base[dev]; + value = readl(&ohci_regs->control); + value |=OHCI_USB_OPER; + writel( value, &ohci_regs->control); + + } +#endif + + return(0); +} + +int wait_transaction( transaction_t *trans) +{ + queue_head_t *qh; + + qh = trans->qh; + + while(!qh->bredth.terminate) + qh = MEM_ADDR(qh->bredth.link); + + return( wait_queue_complete(qh)); +} + +void unlink_transaction( uchar dev, transaction_t *trans) +{ + sched_queue[dev]->depth.terminate=1; + sched_queue[dev]->depth.link = 0; // just in case +} + +int uhci_bulk_transfer( uchar devnum, uchar ep, unsigned int len, uchar *data) +{ + transaction_t *trans; + td_t *td; + int data_len; + int ret; + uchar *buffer; + DPRINTF("bulk_transfer: ep = %x len=%d\n", ep, len); +#if ALLOCATE==1 + buffer = allot2(2048, 0x7ff); + if(buffer==0){ + printf("bulk_transfer: can not allot\n"); + } + memset(buffer,0,2048); +// DPRINTF("bulk_transfer: buffer(virt) = %x buffer(phys) = %x len = %d\n", buffer, virt_to_phys(buffer), len); + + if( !(ep & 0x80)) + memcpy(buffer, data, len); +#else + buffer = data; +#endif + + + trans = _bulk_transfer(devnum, ep, len, buffer); +#if 0 +#ifdef DEBUG + dump_transaction(trans, "bulk_transfer:"); +#endif +#endif + schedule_transaction( usb_device[devnum].controller, trans); + ret = wait_transaction(trans); + + if(ret<0) { +#ifdef DEBUG + dump_uhci(hc_base[usb_device[devnum].controller] ); + dump_td(trans->td_list, "failed_bulk_transaction: "); +#endif + unlink_transaction( usb_device[devnum].controller, trans); + free_transaction(trans); +#if ALLOCATE==1 + forget2(buffer); +#endif + return(-1); + } + + unlink_transaction( usb_device[devnum].controller, trans); + + data_len=0; + td = trans->td_list; + do { + if(td->active) + break; + + if(td->max_transfer == 0x7ff) + break; + + data_len += td->actual +1; + + if(td->actual < td->max_transfer) // short packet also check for errors here + break; + + if(!td->link.terminate){ + td = MEM_ADDR(td->link.link); + } + else { + td=NULL; + } + } while(td); +#if 0 + +#ifdef DEBUG + dump_td(trans->td_list, "bulk_transfer_success:"); +#endif +#endif + + if(data_len < len) { + DPRINTF("bulk_transfer( dev= %d, ep = %d, len = %d, buffer = %x) = %d:short transaction:\n", devnum, ep, len, data, data_len); + dump_td(trans->td_list, "short_transaction:"); + } + + free_transaction(trans); + +#if ALLOCATE==1 + if( (ep & 0x80)) + memcpy(data, buffer, len); + forget2(buffer); +#endif + + + DPRINTF("bulk_transfer returning %d\n", data_len); + return(data_len); +} + +int uhci_control_msg( uchar devnum, uchar request_type, uchar request, unsigned short wValue, unsigned short wIndex, unsigned short wLength, void *data) +{ + transaction_t *trans; + td_t *td; + int data_len=0; + uchar *buffer; + int ret; + DPRINTF("uhci_control_msg: request_type = %x request = %x wLength=%d\n", request_type, request, wLength); +#if ALLOCATE==1 +// if( (wLength!=0) && (data!=NULL) ) { + buffer = allot2(2048+wLength,0x7ff); + if(buffer==0){ + printf("uhci_control_msg: can not allot\n"); + } + + memset(buffer,0,2048+wLength); + //DPRINTF("uhci_control_msg: buffer(virt) = %x buffer(phys) = %x wLength=%d\n", buffer, virt_to_phys(buffer), wLength); + if( !(request_type & 0x80)) + memcpy(buffer, data, wLength); +// } else { +// buffer=NULL; +// } + +#else + buffer = data; +#endif + + trans = ctrl_msg(devnum, request_type, request, wValue, wIndex, wLength, buffer); + if(!trans) { + DPRINTF("uhci_control_msg: ctrl_msg failed!\n"); +#if ALLOCATE==1 + forget2(buffer); +#endif + return(-1); + } + + schedule_transaction( usb_device[devnum].controller, trans); + ret = wait_transaction(trans); + + if(ret<0) { +#ifdef DEBUG + dump_uhci(hc_base[usb_device[devnum].controller] ); + dump_td(trans->td_list, "failed_transaction: "); +#endif + unlink_transaction( usb_device[devnum].controller, trans); + free_transaction(trans); +#if ALLOCATE==1 + forget2(buffer); +#endif + return(ret); + } + +//#ifdef DEBUG +// dump_td(trans->td_list, "success: "); +//#endif + + unlink_transaction( usb_device[devnum].controller, trans); + + // now, see what happened + + if(!trans->qh->depth.terminate) { +// handle setup error + + dump_uhci(hc_base); + dump_td(trans->td_list, "qh->depth failed_transaction: "); + + free_transaction(trans); +#if ALLOCATE==1 + forget2(buffer); +#endif + return(-1); + } + + td = trans->td_list; + + do { + if(td->packet_type != SETUP_TOKEN) + data_len += td->actual; + + if(td->actual < td->max_transfer) // short packet also check for errors here + break; + + if(!td->link.terminate) { + td = MEM_ADDR(td->link.link); + } + else { + td=NULL; + } + } while(td); + + free_transaction(trans); + +#if ALLOCATE==1 + if ( (wLength!=0) && (data!=NULL)){ + if( (request_type & 0x80)) + memcpy(data, buffer, wLength); + forget2(buffer); + } +#endif + + DPRINTF("usb_control_message returning %d\n", data_len); + + return(data_len); +} + + +int poll_u_root_hub(unsigned short port, uchar controller) +{ + ushort value; + int addr=0; + int i; + static int do_over=0; + + value = inw(port); + + debug("poll_u_root_hub1 v=%08x\t", value); + + if(value & 0x02 || do_over == port) { + debug("poll_u_root_hub2 v=%08x\t", value); + do_over=0; + if(value & 0x01 ) { // if port connected + debug("poll_u_root_hub21 v=%08x\t", value); + DPRINTF("Connection on port %04x\n", port); + + outw(value, port); + for(i=0; i<40; i++) { + udelay(10000+usec_offset); + value = inw(port); + if(value & 0x02) { + outw(value, port); + i=0; + DPRINTF("BOUNCE!\n"); + } + } + + uport_wakeup(port); +// DPRINTF("Wakup %04x\n", port); + + uport_reset(port); + udelay(10); + uport_enable(port); + + if(!value & 0x01) { + DPRINTF("Device went away!\n"); + return(-1); + } + + addr = configure_device( port, controller, value & 0x100); + + if(addr<0) { + uport_disable(port); + udelay(20000); +// uport_reset(port); + uport_reset_long(port); + uport_suspend(port); + do_over=port; + uhc_clear_stat(); +// dump_uhci(0x38c0); + } + } else { + uport_suspend(port); + uport_disable(port); + DPRINTF("Port %04x disconnected\n", port); + // wave hands, deconfigure devices on this port! + } + } + + + return(addr); +} + +#endif diff --git a/src/filo/usb/uhci.h b/src/filo/usb/uhci.h new file mode 100644 index 00000000..17370a13 --- /dev/null +++ b/src/filo/usb/uhci.h @@ -0,0 +1,175 @@ +#ifndef _UHCI_H +#define _UHCI_H + +/* + * The link pointer is multi use. Some fields are valid only for some uses. + * In other cases, they must be 0 + * + */ + +#define MAX_POLLDEV 10 + +#define MAX_TRANSACTIONS 10 +#define MAX_QUEUEHEAD 255 +#define MAX_TD 1024 + + +typedef struct link_pointer { + unsigned long terminate:1; + unsigned long queue:1; + unsigned long depth:1; + unsigned long reserved:1; + unsigned long link:28; +} __attribute__((packed)) link_pointer_t; + +extern link_pointer_t *frame_list[]; + +void init_framelist(uchar dev); + + +#define SETUP_TOKEN 0x2d +#define IN_TOKEN 0x69 +#define OUT_TOKEN 0xe1 + +#define CTRL_RETRIES 3 +#define CONTROL_STS_RETRIES 0 + + +// some port features +#define PORT_CONNECTION 0 +#define PORT_ENABLE 1 +#define PORT_SUSPEND 2 +#define PORT_OVER_CURRENT 3 +#define PORT_RESET 4 +#define PORT_POWER 8 +#define PORT_LOW_SPEED 9 +#define C_PORT_CONNECTION 16 +#define C_PORT_ENABLE 17 +#define C_PORT_SUSPEND 18 +#define C_PORT_OVER_CURRENT 19 +#define C_PORT_RESET 20 + +// features +#define FEATURE_HALT 0 + +typedef struct td { + + link_pointer_t link; + + unsigned long actual:11; // actual length + unsigned long reserved2:5; + +// status/error flags + unsigned long res1:1; + unsigned long bitstuff:1; + unsigned long crc:1; + unsigned long nak:1; + unsigned long babble:1; + unsigned long buffer_error:1; + unsigned long stall:1; + unsigned long active:1; + + unsigned long interrupt:1; // interrupt on complete + unsigned long isochronous:1; + unsigned long lowspeed:1; + unsigned long retrys:2; + unsigned long detect_short:1; + unsigned long reserved3:2; + + unsigned long packet_type:8; // one of in (0x69), out (0xe1) or setup (0x2d) + unsigned long device_addr:7; + unsigned long endpoint:4; + unsigned long data_toggle:1; + unsigned long reserved:1; + unsigned long max_transfer:11; // misnamed. Desired length might be better + + void *buffer; + unsigned long data[4]; // free use by driver +} __attribute__((packed)) td_t; + +typedef struct queue_head { + link_pointer_t bredth; // depth must = 0 + link_pointer_t depth; // depth may vary randomly, ignore + unsigned long int udata[2]; +} __attribute__((packed)) queue_head_t; + +typedef struct transaction { + queue_head_t *qh; + td_t *td_list; + struct transaction *next; +} transaction_t; + +//##################################################### +int wait_head( queue_head_t *head, int count); + +extern queue_head_t *free_qh; +extern queue_head_t *queue_heads; + +queue_head_t *new_queue_head(void); +void free_queue_head( queue_head_t *qh); +void init_qh(void); + +extern td_t *free_td_list; +extern td_t *tds; + +void init_td(void); +td_t *new_td(void); +td_t *find_last_td(td_t *td); +void free_td( td_t *td); +link_pointer_t *queue_end( queue_head_t *queue); +void add_td( queue_head_t *head, td_t *td); + +extern transaction_t transactions[MAX_TRANSACTIONS]; +extern transaction_t *free_transactions; + +void init_transactions(void); +void free_transaction( transaction_t *trans ); +transaction_t *new_transaction(td_t *td); +transaction_t *add_transaction( transaction_t *trans, td_t *td); + + +#define USBCMD(x) hc_base[x] +#define USBSTS(x) (hc_base[x] + 0x02) +#define USBINTR(x) (hc_base[x] + 0x04) +#define FRNUM(x) ( hc_base[x] + 0x06) +#define FLBASE(x) ( hc_base[x] + 0x08) +#define SOFMOD(x) ( hc_base[x] + 0x0c) +#define PORTSC1(x) ( hc_base[x] + 0x10) +#define PORTSC2(x) ( hc_base[x] + 0x12) + +#define USBCMDRUN 0x01 +#define USBCMD_DEBUG 0x20 + +#define USBSTSHALTED 0x20 + + +void hc_reset(uchar dev); +int hc_stop(void); +int hc_start(uchar dev); + +extern queue_head_t *sched_queue[]; + +void init_sched(uchar dev); +int poll_queue_head( queue_head_t *qh); +int wait_queue_complete( queue_head_t *qh); + +extern int num_polls; +extern int (*devpoll[MAX_POLLDEV])(uchar); +extern uchar parm[MAX_POLLDEV]; + +transaction_t *_bulk_transfer( uchar devnum, uchar ep, unsigned int len, uchar *data); +transaction_t *ctrl_msg(uchar devnum, uchar request_type, uchar request, unsigned short wValue, unsigned short wIndex, unsigned short wLength, uchar *data); +int schedule_transaction( uchar dev, transaction_t *trans); +int wait_transaction( transaction_t *trans); +void unlink_transaction( uchar dev, transaction_t *trans); +int uhci_bulk_transfer( uchar devnum, uchar ep, unsigned int len, uchar *data); +int uhci_control_msg( uchar devnum, uchar request_type, uchar request, unsigned short wValue, unsigned short wIndex, unsigned short wLength, void *data); + + +// defined in uhci.c +int uhc_init(struct pci_device *dev); +void uhci_init(void); +void clear_uport_stat(unsigned short port); +int poll_u_root_hub(unsigned short port, uchar controller); + +#endif diff --git a/src/filo/usb/usb.c b/src/filo/usb/usb.c new file mode 100644 index 00000000..23afe9a7 --- /dev/null +++ b/src/filo/usb/usb.c @@ -0,0 +1,803 @@ +#ifdef USB_DISK + +/******************************************************************************* + * + * + * Copyright 2003 Steven James and + * LinuxLabs http://www.linuxlabs.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. + * + ******************************************************************************/ + +#include +#include +#include +#include + +#define DEBUG_THIS DEBUG_USB +#include + +#define DPRINTF debug + + +#include "usb.h" +#include "uhci.h" +#include "ohci.h" +#include "debug_x.h" + + +#define ALLOCATE 1 + +int usec_offset=0; + +int num_controllers=0; + +uint32_t hc_base[MAX_CONTROLLERS]; +uint8_t hc_type[MAX_CONTROLLERS]; + + +void hci_init(void) +{ + int i; + struct pci_device *dev; + uint8_t prog_if; + + + for(i=0;iclass>>8) & 0xff); + if(prog_if == 0x00 ) { // UHCI + hc_type[num_controllers] = prog_if; + uhc_init(dev); + } + else if(prog_if == 0x10) { // OHCI + hc_type[num_controllers] = prog_if; + ohc_init(dev); + } +#if 0 + else if(prog_if == 0x20) { // EHCI + hc_type[num_controllers] = prog_if; + ehc_init(dev); + } +#endif + i++; + } + // From now should not change num_controllers any more + + uhci_init(); + ohci_init(); +} + + +int next_usb_dev; +usbdev_t usb_device[MAX_USB_DEV]; + +void init_devices(void) +{ + + memset(usb_device,0,sizeof(usb_device)); + usb_device[0].max_packet[0] = 8; + next_usb_dev=2; // 0 for all controller root hub, use MAX_CONTROLLERS instead??? + // do we need have one for every controller ?? or just use hc_base and hc_type instead + // For example 0 --> controller 1 root hub + // 1 --> controller 2 root hub + // 2 --> controller 3 root hub.... +} + + +inline int set_address( uchar address) +{ + int ret; + + ret = usb_control_msg(0, 0, SET_ADDRESS, address, 0, 0, NULL); + + return(ret); +} + +inline int clear_stall(uchar device, uchar endpoint) +{ + int ret; + + ret = usb_control_msg(device, CONTROL_ENDPOINT, CLEAR_FEATURE, FEATURE_HALT, endpoint, 0, NULL); + if(hc_type[device]==0x00) { + usb_device[device].toggle[endpoint]=0; + } + else if(hc_type[device]==0x10) { + usb_settoggle(&usb_device[device], endpoint & 0xf, ((endpoint & 0x80)>>7)^1, 0); + } + + return(ret); +} + +inline int device_reset(uchar device) { + return usb_control_msg(device, 0x21, 0xff, 0, 0, 0, NULL); +} + +/////////////////////////////////////////////////////////////////////////////////////// +// +// String Descriptors +// +////////////////////////////////////////////////////////////////////////////////////// + +#define STRING_DESCRIPTOR 0x0300 + +int get_string( uchar addr, uchar string, int len, uchar *buffer) +{ + int ret; + int i,j; + int real_len; + ushort lang; + + if(!string) { + strcpy(buffer, "unknown"); + return(0); + } + + ret = usb_control_msg(addr, 0x80, GET_DESCRIPTOR, STRING_DESCRIPTOR | string, 0, 4, buffer); + real_len = buffer[0]; + if(real_len>len) + real_len = len; + + lang = buffer[2] | buffer[3]<<8; + ret = usb_control_msg(addr, 0x80, GET_DESCRIPTOR, STRING_DESCRIPTOR | string, lang, real_len, buffer); + + // de-unicode it! + for(i=0, j=2; jlen) + real_len = len; + + if(real_len<=4) { + strcpy(buffer, "USB"); + real_len = 3; + buffer[real_len] = 0; + } else { + // de-unicode it! + for(i=0, j=2; jbNbrPorts; i++) { + ret = usb_control_msg(addr, 0xa3, GET_STATUS, 0x0, i, 4, &status); +// DPRINTF("Get status for port %u returns: %d\n", i, ret); +// dump_hex(&status, 4, "status="); + + if(status.change.c_port_connection) { + ret = usb_control_msg(addr, 0x23, CLEAR_FEATURE, C_PORT_CONNECTION, i, 0, NULL); // clear status + + if(status.stat.port_connection) { + udelay(desc->bPwrOn2PwrGood * 20000); + + hub_port_resume(addr, i); + + ret = hub_port_reset(addr,i); + udelay(10); + ret = usb_control_msg(addr, 0x23, SET_FEATURE, PORT_ENABLE, i, 0, NULL); // enable port + +// ret = usb_control_msg(addr, 0xa3, GET_STATUS, 0x0, i, 4, &status); +// DPRINTF("*****Get status again for port %u returns: %d\n", i, ret); +// dump_hex(&status, 4, "status="); + + devaddr = configure_device(i, usb_device[addr].controller, status.stat.port_lowspeed); + + // configure + } else { + ret = usb_control_msg(addr, 0x23, SET_FEATURE, PORT_SUSPEND, i, 0, NULL); // suspend port + ret = usb_control_msg(addr, 0x23, CLEAR_FEATURE, PORT_ENABLE, i, 0, NULL); // disable port + DPRINTF("Hub %d, Port %04x disconnected\n", addr, i); + // deconfigure + } + } + } + return(devaddr); + +} + +int usb_hub_init( uchar addr) +{ + int i; + int ret; + hub_descriptor_t *desc; + + desc = allot(sizeof(hub_descriptor_t)); + + memset(desc, 0 , sizeof(hub_descriptor_t)); + + DPRINTF("hub init (%d)\n", addr); + + ret = usb_control_msg(addr, 0xa0, GET_DESCRIPTOR, 0x2900, 0, 8, desc); + ret = usb_control_msg(addr, 0xa0, GET_DESCRIPTOR, 0x2900, 0, desc->bLength, desc); + + usb_device[addr].private = desc; + + for(i=1; i<=desc->bNbrPorts; i++) + ret = usb_control_msg(addr, 0x23, SET_FEATURE, PORT_POWER, i, 0, NULL); // power port + + + // register hub to be polled + + devpoll[num_polls] = poll_hub; + parm[num_polls++] = addr; + + return(0); +} + +extern void ohci_dump_x(uchar controller); + +// will set up whatever device is answering at address 0. +int configure_device(uint32_t port, uchar controller, unsigned int lowspeed) +{ + device_descriptor_t *desc; + config_descriptor_t *conf; + interface_descriptor_t *iface; + endpoint_descriptor_t *epd; + int ret; + int i; + int addr = next_usb_dev++; + uchar buffer[512]; + uchar string[255]; + ushort lang; + uchar x[2]; + + desc = (device_descriptor_t *) buffer; + + memset( &usb_device[addr], 0, sizeof(usbdev_t)); + + printf("New USB device, setting address %d\n", addr); + if(lowspeed) { + usb_device[addr].lowspeed = usb_device[0].lowspeed = 1; + DPRINTF("LOWSPEED\n"); + } else + usb_device[addr].lowspeed = usb_device[0].lowspeed = 0; + + usb_device[0].port = usb_device[addr].port = port; + usb_device[0].controller = usb_device[addr].controller = controller; + usb_device[addr].toggle2[0]=0; + usb_device[addr].toggle2[1]=0; + +// hc_clear_stat(); + + ret = set_address(addr); + if(ret<0) { + DPRINTF("configure_device: set_address failed!\n"); + next_usb_dev--; + return(-1); + } + + mdelay(10); /* Let the SET_ADDRESS settle */ + + usb_device[addr].max_packet[0] = 8; + + + DPRINTF("Fetching device descriptor length\n"); + ret = usb_control_msg(addr, 0x80, GET_DESCRIPTOR, 0x100, 0, 8, desc); + + usb_device[addr].max_packet[0] = desc->max_packet; + + DPRINTF("Fetching device descriptor\n"); + ret = usb_control_msg(addr, 0x80, GET_DESCRIPTOR, 0x100, 0, desc->bLength, desc); + if(ret < desc->bLength) + return(-1); + + DPRINTF("Fetching config descriptor length\n"); + conf = (config_descriptor_t *) (buffer + sizeof(device_descriptor_t)); + + ret = usb_control_msg(addr, 0x80, GET_DESCRIPTOR, 0x200, 0, 8, conf); + + DPRINTF("Fetching config descriptor\n"); + ret = usb_control_msg(addr, 0x80, GET_DESCRIPTOR, 0x200, 0, conf->wTotalLength, conf); + if(ret < conf->wTotalLength) + return(-1); + + iface = (interface_descriptor_t *) (buffer + sizeof(device_descriptor_t) + conf->bLength); + epd = (endpoint_descriptor_t *) (buffer + conf->bLength + iface->bLength + sizeof(device_descriptor_t)); + + DPRINTF("device:\n"); + dump_device_descriptor( desc, ""); + DPRINTF("config:\n"); + dump_config_descriptor( (uchar *)conf, ""); + + DPRINTF("Selecting Configuration number %x:\n", conf->bConfigurationValue); + ret = usb_control_msg(addr, 0, SET_CONFIGURATION, conf->bConfigurationValue, 0, 0, NULL); + +// mdelay(20); + +#if 0 + usb_control_msg(addr, 0x80, GET_CONFIGURATION, 0, 0, 1 , x); + DPRINTF("Configuration number = %x\n", x[0]); + + usb_control_msg(addr, 0x80, GET_STATUS, 0, addr, 2, x); + DPRINTF("status = %x %x\n", x[0], x[1]); + + usb_control_msg(addr, 0x81, GET_STATUS, 0, 0, 2, x); + DPRINTF("status = %x %x\n", x[0], x[1]); +#endif + + for(i=0; ibNumEndpoints;i++) { + if(!epd[i].bEndpointAddress) { + usb_device[addr].max_packet[ 1 ] = epd[i].wMaxPacketSize & 0x3ff; + } else { + usb_device[addr].max_packet[ epd[i].bEndpointAddress & 0x7f ] = epd[i].wMaxPacketSize & 0x3ff; + } + + if( (epd[i].bmAttributes & 0x03) == 0x01) // interrupt + usb_device[addr].interrupt = epd[i].bEndpointAddress; + + if( (epd[i].bmAttributes & 0x03) == 0x02) { // bulk +#if 0 + DPRINTF("clear stall on ep=%x\n", epd[i].bEndpointAddress); + clear_stall(addr, epd[i].bEndpointAddress); // to reset data toggle + udelay(10); +#endif + +#if 0 + usb_control_msg(addr, 0x82, GET_STATUS, 0, epd[i].bEndpointAddress, 2, x); + DPRINTF("status = %x %x\n", x[0], x[1]); +#endif + + if(epd[i].bEndpointAddress & 0x80){ //in + usb_device[addr].bulk_in = epd[i].bEndpointAddress; + } + else { //out + usb_device[addr].bulk_out = epd[i].bEndpointAddress; + } + } + + } + + // determine device class + if(desc->Class) { + usb_device[addr].class = desc->Class; + usb_device[addr].subclass = desc->SubClass; + usb_device[addr].protocol = desc->protocol; + } else { + usb_device[addr].class = iface->bInterfaceClass; + usb_device[addr].subclass = iface->bInterfaceSubClass; + usb_device[addr].protocol = iface->bInterfaceProtocol; + } + + printf("%02x:%02x:%02x\n", usb_device[addr].class, usb_device[addr].subclass, usb_device[addr].protocol); +#if 0 + get_string(addr, desc->iManufacturor, sizeof(string), string); + printf("Manufacturor: %s\n", string); + + get_string(addr, desc->iProduct, sizeof(string), string); + printf("Product: %s\n", string); + + get_string(addr, desc->iSerial, sizeof(string), string); + printf("Serial: %s\n", string); +#else + lang = get_lang(addr, 0, sizeof(string), string); + + get_string2(addr, desc->iManufacturor, lang, sizeof(string), string); + printf("Manufacturor: %s\n", string); + + get_string2(addr, desc->iProduct, lang,sizeof(string), string); + printf("Product: %s\n", string); + + get_string2(addr, desc->iSerial, lang, sizeof(string), string); + printf("Serial: %s\n", string); +#endif + + switch( usb_device[addr].class) { + case 0x09: // hub + usb_hub_init(addr); + break; + + default: + break; + + } + + DPRINTF("DEVICE CONFIGURED\n"); + + return(addr); +} + +int num_polls=0; +int (*devpoll[MAX_POLLDEV])(uchar); +uchar parm[MAX_POLLDEV]; + +int poll_usb() +{ + int addr; + int found=0; + int i; + int j; + + for(i=0; i0, should probably see what was attached! + if(hc_type[i]==0x00) { + addr = poll_u_root_hub(PORTSC1(i), i); + if(addr && !found) + found=addr; + + addr = poll_u_root_hub(PORTSC2(i), i); + if(addr && !found) + found=addr; + } + + else if(hc_type[i]==0x10) { + int NDP; + NDP = readl(&ohci_regs->roothub.a) & 0xff; + ohci_regs = (ohci_regs_t *)hc_base[i]; + for(j=0;jroothub.portstatus[j], i); + if(addr && !found) + found=addr; + } + + } + + } + + // now poll registered drivers (such as the hub driver + for(i=0;iurb; + } else { + urb = NULL; + } +#endif + + memset(urb, 0, sizeof(*urb)); + + return urb; +} +/** + * usb_free_urb - frees the memory used by a urb + * @urb: pointer to the urb to free + * + * If an urb is created with a call to usb_create_urb() it should be + * cleaned up with a call to usb_free_urb() when the driver is finished + * with it. + */ +void usb_free_urb(struct urb* urb) +{ +#if URB_PRE_ALLOCATE!=1 + if (urb) + forget2(urb); +#endif +} + +void usb_wait_urb_done(struct urb* urb, int timeout) +{ + usbdev_t *usb_dev = urb->dev; + if(hc_type[usb_dev->controller]==0x10) { + ohci_wait_urb_done(urb, timeout); + } + +} + + +int usb_submit_urb(struct urb *urb) +{ + if (urb && urb->dev) { +#if 0 + if(hc_type[urb->dev->controller] == 0x00) { + return uhci_submit_urb(urb); + } else +#endif + if(hc_type[urb->dev->controller] == 0x10) { + return ohci_submit_urb(urb); + } +#if 0 + else if(hc_type[urb->dev->controller] == 0x20) { + return ohci_submit_urb(urb); + } +#endif + return 0; + } + else + return -ENODEV; +} + +// Starts urb and waits for completion or timeout +static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length) +{ + int status; + status = usb_submit_urb(urb); + +//for OHCI We will check the BLF and CLF, because HC after processing all td list, it will clear the BLF and CLF + usb_wait_urb_done(urb, timeout); +//Add by LYH to call complete function + if(urb->complete!=0) urb->complete(urb); + + if (actual_length) + *actual_length = urb->actual_length; + + usb_free_urb(urb); + return status; +} +// returns status (negative) or length (positive) +int usb_internal_control_msg(struct usbdev *usb_dev, unsigned int pipe, + struct usb_ctrlrequest *cmd, void *data, int len, int timeout, usb_complete_t complete) +{ + struct urb *urb; + int retv; + int length; + + urb = usb_alloc_urb(usb_dev->controller); + if (!urb) + return -ENOMEM; + + FILL_CONTROL_URB(urb, usb_dev, pipe, (unsigned char*)cmd, data, len, + complete,0); + + retv = usb_start_wait_urb(urb, timeout, &length); + if (retv < 0) + return retv; + else + return length; +} +int usb_control_msg_x(struct usbdev *dev, unsigned int pipe, u8 request, u8 requesttype, + u16 value, u16 index, void *data, u16 size, int timeout, usb_complete_t complete) +{ + struct usb_ctrlrequest *dr; + int ret; + int controller = dev->controller; + ohci_t *ohci; + +#if URB_PRE_ALLOCATE!=1 + dr = allot2(sizeof(struct usb_ctrlrequest), 0xf); + if (!dr) { + printf("usb_control_msg_x: dr allocate no MEM\n"); + return -ENOMEM; + } +#else + if(hc_type[controller] == 0x10) { //OHCI + ohci = &_ohci_x[controller]; + dr = ohci->dr; + } else { + dr = NULL; + } + +#endif + + dr->bRequestType = requesttype; + dr->bRequest = request; + dr->wValue = cpu_to_le16p(&value); + dr->wIndex = cpu_to_le16p(&index); + dr->wLength = cpu_to_le16p(&size); + + ret = usb_internal_control_msg(dev, pipe, dr, data, size, timeout, complete); + +#if URB_PRE_ALLOCATE!=1 + forget2(dr); +#endif + + return ret; +} +int usb_bulk_msg_x(struct usbdev *usb_dev, unsigned int pipe, + void *data, int len, int *actual_length, int timeout, usb_complete_t complete) +{ + struct urb *urb; + + if (len < 0) + return -EINVAL; + + urb=usb_alloc_urb(usb_dev->controller); + if (!urb) + return -ENOMEM; + + FILL_BULK_URB(urb, usb_dev, pipe, data, len, + complete, 0); + + return usb_start_wait_urb(urb,timeout,actual_length); +} + +#endif diff --git a/src/filo/usb/usb.h b/src/filo/usb/usb.h new file mode 100644 index 00000000..58fd5734 --- /dev/null +++ b/src/filo/usb/usb.h @@ -0,0 +1,435 @@ +#ifndef _USB_H +#define _USB_H + +#define URB_PRE_ALLOCATE 1 + +#define u32 uint32_t +#define u16 uint16_t +#define u8 uint8_t + +#define uchar uint8_t +#define ushort uint16_t +#define EBUSY 1 +#define ENOMEM 12 +#define ENODEV 19 +#define EINVAL 22 +#define EINPROGRESS 115 + +#define LINK_ADDR(x) ( virt_to_bus(x) >> 4) +#define MEM_ADDR(x) (void *) ( bus_to_virt( ((unsigned int) (x)) <<4) ) + +#define MAX_CONTROLLERS 4 + +extern int num_controllers; + +extern uint32_t hc_base[]; +extern uint8_t hc_type[]; + +// Some control message bmRequestType defines +#define CTRL_DEVICE 0 +#define CONTROL_INTERFACE 1 +#define CONTROL_ENDPOINT 2 +#define CONTROL_OTHER 3 +#define CONTROL_RECIPIENT_MASK 0x1f + +#define CONTROL_TYPE_STD 0 +#define CONTROL_TYPE_CLASS 0x20 +#define CONTROL_CLASS_VENDOR 0x40 +#define CONTROL_CLASS_MASK 0x60 + +#define CONTROL_OUT 0 +#define CONTROL_IN 0x80 +#define CONTROL_DIR_MASK 0x80 + +// bRequest values +#define GET_STATUS 0 +#define CLEAR_FEATURE 1 +#define SET_FEATURE 3 +#define SET_ADDRESS 5 + +#define GET_DESCRIPTOR 6 +#define SET_DESCRIPTOR 7 + +#define GET_CONFIGURATION 8 +#define SET_CONFIGURATION 9 + +#define GET_INTERFACE 10 +#define SET_INTERFACE 11 + +#define SYNC_FRAME 12 + +// descriptor types +#define DEVICE_DESC 1 +#define CONFIGURATION_DESC 2 +#define STRING_DESC 3 +#define INTERFACE_DESC 4 +#define ENDPOINT_DESC 5 +#define OTHERSPEED_DESC 7 +#define POWER_DESC 8 + + +typedef struct device_descriptor { + uchar bLength; + uchar type; + + uchar bcdVersion[2]; + uchar Class; + uchar SubClass; + uchar protocol; + uchar max_packet; + + unsigned short idVendor; + unsigned short idProduct; + + uchar bcdDevice[2]; + uchar iManufacturor; + uchar iProduct; + uchar iSerial; + uchar bNumConfig; +} __attribute__((packed)) device_descriptor_t; + +#define GET_DESCRIPTOR 6 + +typedef struct config_descriptor { + uchar bLength; + uchar type; + + unsigned short wTotalLength; + uchar bNumInterfaces; + uchar bConfigurationValue; + uchar iConfiguration; + + uchar bmAttributes; + uchar bMaxPower; +} __attribute__((packed)) config_descriptor_t; + +typedef struct interface_descriptor { + uchar bLength; + uchar type; + + uchar bInterfaceNumber; + uchar bAlternateSetting; + + uchar bNumEndpoints; + uchar bInterfaceClass; + uchar bInterfaceSubClass; + uchar bInterfaceProtocol; + uchar iInterface; +} __attribute__((packed)) interface_descriptor_t; + +typedef struct endpoint_descriptor { + uchar bLength; + uchar type; + + uchar bEndpointAddress; + uchar bmAttributes; + unsigned short wMaxPacketSize; + uchar bInterval; +} __attribute__((packed)) endpoint_descriptor_t; + +typedef struct ctrl_msg { + uchar bmRequestType; + uchar bRequest; + unsigned short wValue; + unsigned short wIndex; + unsigned short wLength; +} __attribute__((packed)) ctrl_msg_t; + +// Some descriptors for hubs, will be moved later +typedef struct hub_descriptor { + uchar bLength; + uchar type; + + uchar bNbrPorts; + ushort wHubCharacteristics; + uchar bPwrOn2PwrGood; + uchar bHubCntrCurrent; + + uchar DeviceRemovable; // assume bNbrPorts <=8 + uchar PortPwrCntrMask; +} __attribute__((packed)) hub_descriptor_t; + +#define MAX_USB_DEV 127 +#define MAX_EP 8 + +typedef struct usbdev { + uint32_t port; + uchar address; + uchar controller; + uchar class; + uchar subclass; + uchar protocol; + uchar bulk_in; + uchar bulk_out; + uchar interrupt; + uchar lowspeed; + uint32_t toggle2[2]; //For OHCI + uint32_t halted[2]; + uchar toggle[MAX_EP]; //for UHCI + unsigned short max_packet[MAX_EP]; + void *private; +} usbdev_t; + +// I will use urb as transaction for OHCI to remember the td and ed + +struct urb; +typedef void (*usb_complete_t)(struct urb *); + +struct urb +{ +#if 0 + spinlock_t lock; // lock for the URB +#endif + void *hcpriv; // private data for host controller +#if 0 + struct list_head urb_list; // list pointer to all active urbs + struct urb *next; // pointer to next URB +#endif + struct usbdev *dev; // pointer to associated USB device + unsigned int pipe; // pipe information + int status; // returned status + unsigned int transfer_flags; // USB_DISABLE_SPD | USB_ISO_ASAP | etc. + void *transfer_buffer; // associated data buffer + void *transfer_dma; // dma addr for transfer_buffer + int transfer_buffer_length; // data buffer length + int actual_length; // actual data buffer length + int bandwidth; // bandwidth for this transfer request (INT or ISO) + unsigned char *setup_packet; // setup packet (control only) + void * setup_dma; // dma addr for setup_packet + // + int start_frame; // start frame (iso/irq only) + int number_of_packets; // number of packets in this request (iso) + int interval; // polling interval (irq only) + int error_count; // number of errors in this transfer (iso only) + int timeout; // timeout (in jiffies) + // + void *context; // context for completion routine + usb_complete_t complete; // pointer to completion routine + // +#if 0 + struct iso_packet_descriptor iso_frame_desc[0]; +#endif +}; + +typedef struct urb urb_t; + +/* + * urb->transfer_flags: + */ +#define USB_DISABLE_SPD 0x0001 +#define URB_SHORT_NOT_OK USB_DISABLE_SPD +#define USB_ISO_ASAP 0x0002 +#define USB_ASYNC_UNLINK 0x0008 +#define USB_QUEUE_BULK 0x0010 +#define USB_NO_FSBR 0x0020 +#define USB_ZERO_PACKET 0x0040 // Finish bulk OUTs always with zero length packet +#define URB_NO_INTERRUPT 0x0080 /* HINT: no non-error interrupt needed */ + /* ... less overhead for QUEUE_BULK */ +#define USB_TIMEOUT_KILLED 0x1000 // only set by HCD! + + +struct usb_ctrlrequest { + u8 bRequestType; + u8 bRequest; + u16 wValue; + u16 wIndex; + u16 wLength; +} __attribute__ ((packed)); + +/* + * USB-status codes: + * USB_ST* maps to -E* and should go away in the future + */ + +#define USB_ST_NOERROR 0 +#define USB_ST_CRC (-EILSEQ) +#define USB_ST_BITSTUFF (-EPROTO) +#define USB_ST_NORESPONSE (-ETIMEDOUT) /* device not responding/handshaking */ +#define USB_ST_DATAOVERRUN (-EOVERFLOW) +#define USB_ST_DATAUNDERRUN (-EREMOTEIO) +#define USB_ST_BUFFEROVERRUN (-ECOMM) +#define USB_ST_BUFFERUNDERRUN (-ENOSR) +#define USB_ST_INTERNALERROR (-EPROTO) /* unknown error */ +#define USB_ST_SHORT_PACKET (-EREMOTEIO) +#define USB_ST_PARTIAL_ERROR (-EXDEV) /* ISO transfer only partially completed */ +#define USB_ST_URB_KILLED (-ENOENT) /* URB canceled by user */ +#define USB_ST_URB_PENDING (-EINPROGRESS) +#define USB_ST_REMOVED (-ENODEV) /* device not existing or removed */ +#define USB_ST_TIMEOUT (-ETIMEDOUT) /* communication timed out, also in urb->status**/ +#define USB_ST_NOTSUPPORTED (-ENOSYS) +#define USB_ST_BANDWIDTH_ERROR (-ENOSPC) /* too much bandwidth used */ +#define USB_ST_URB_INVALID_ERROR (-EINVAL) /* invalid value/transfer type */ +#define USB_ST_URB_REQUEST_ERROR (-ENXIO) /* invalid endpoint */ +#define USB_ST_STALL (-EPIPE) /* pipe stalled, also in urb->status*/ + +/** + * FILL_CONTROL_URB - macro to help initialize a control urb + * @URB: pointer to the urb to initialize. + * @DEV: pointer to the struct usb_device for this urb. + * @PIPE: the endpoint pipe + * @SETUP_PACKET: pointer to the setup_packet buffer + * @TRANSFER_BUFFER: pointer to the transfer buffer + * @BUFFER_LENGTH: length of the transfer buffer + * @COMPLETE: pointer to the usb_complete_t function + * @CONTEXT: what to set the urb context to. + * + * Initializes a control urb with the proper information needed to submit + * it to a device. This macro is depreciated, the usb_fill_control_urb() + * function should be used instead. + */ +#define FILL_CONTROL_URB(URB,DEV,PIPE,SETUP_PACKET,TRANSFER_BUFFER,BUFFER_LENGTH,COMPLETE,CONTEXT) \ + do {\ + (URB)->dev=DEV;\ + (URB)->pipe=PIPE;\ + (URB)->setup_packet=SETUP_PACKET;\ + (URB)->transfer_buffer=TRANSFER_BUFFER;\ + (URB)->transfer_buffer_length=BUFFER_LENGTH;\ + (URB)->complete=COMPLETE;\ + (URB)->context=CONTEXT;\ + } while (0) + + +/** + * FILL_BULK_URB - macro to help initialize a bulk urb + * @URB: pointer to the urb to initialize. + * @DEV: pointer to the struct usb_device for this urb. + * @PIPE: the endpoint pipe + * @TRANSFER_BUFFER: pointer to the transfer buffer + * @BUFFER_LENGTH: length of the transfer buffer + * @COMPLETE: pointer to the usb_complete_t function + * @CONTEXT: what to set the urb context to. + * + * Initializes a bulk urb with the proper information needed to submit it + * to a device. This macro is depreciated, the usb_fill_bulk_urb() + * function should be used instead. + */ +#define FILL_BULK_URB(URB,DEV,PIPE,TRANSFER_BUFFER,BUFFER_LENGTH,COMPLETE,CONTEXT) \ + do {\ + (URB)->dev=DEV;\ + (URB)->pipe=PIPE;\ + (URB)->transfer_buffer=TRANSFER_BUFFER;\ + (URB)->transfer_buffer_length=BUFFER_LENGTH;\ + (URB)->complete=COMPLETE;\ + (URB)->context=CONTEXT;\ + } while (0) + + +/* + * USB directions + */ +#define USB_DIR_OUT 0 /* to device */ +#define USB_DIR_IN 0x80 /* to host */ + +/* + * USB Packet IDs (PIDs) + */ +#define USB_PID_UNDEF_0 0xf0 +#define USB_PID_OUT 0xe1 +#define USB_PID_ACK 0xd2 +#define USB_PID_DATA0 0xc3 +#define USB_PID_PING 0xb4 /* USB 2.0 */ +#define USB_PID_SOF 0xa5 +#define USB_PID_NYET 0x96 /* USB 2.0 */ +#define USB_PID_DATA2 0x87 /* USB 2.0 */ +#define USB_PID_SPLIT 0x78 /* USB 2.0 */ +#define USB_PID_IN 0x69 +#define USB_PID_NAK 0x5a +#define USB_PID_DATA1 0x4b +#define USB_PID_PREAMBLE 0x3c /* Token mode */ +#define USB_PID_ERR 0x3c /* USB 2.0: handshake mode */ +#define USB_PID_SETUP 0x2d +#define USB_PID_STALL 0x1e +#define USB_PID_MDATA 0x0f /* USB 2.0 */ + +#define PIPE_ISOCHRONOUS 0 +#define PIPE_INTERRUPT 1 +#define PIPE_CONTROL 2 +#define PIPE_BULK 3 + +#define usb_maxpacket(dev, pipe, out) ((dev)->max_packet[usb_pipeendpoint(pipe)]) +#define usb_packetid(pipe) (((pipe) & USB_DIR_IN) ? USB_PID_IN : USB_PID_OUT) + +#define usb_pipeout(pipe) ((((pipe) >> 7) & 1) ^ 1) +#define usb_pipein(pipe) (((pipe) >> 7) & 1) +#define usb_pipedevice(pipe) (((pipe) >> 8) & 0x7f) +#define usb_pipe_endpdev(pipe) (((pipe) >> 8) & 0x7ff) +#define usb_pipeendpoint(pipe) (((pipe) >> 15) & 0xf) +#define usb_pipedata(pipe) (((pipe) >> 19) & 1) +#define usb_pipeslow(pipe) (((pipe) >> 26) & 1) +#define usb_pipetype(pipe) (((pipe) >> 30) & 3) +#define usb_pipeisoc(pipe) (usb_pipetype((pipe)) == PIPE_ISOCHRONOUS) +#define usb_pipeint(pipe) (usb_pipetype((pipe)) == PIPE_INTERRUPT) +#define usb_pipecontrol(pipe) (usb_pipetype((pipe)) == PIPE_CONTROL) +#define usb_pipebulk(pipe) (usb_pipetype((pipe)) == PIPE_BULK) + +#define PIPE_DEVEP_MASK 0x0007ff00 + + +/* The D0/D1 toggle bits */ +#define usb_gettoggle(dev, ep, out) (((dev)->toggle2[out] >> (ep)) & 1) +#define usb_dotoggle(dev, ep, out) ((dev)->toggle2[out] ^= (1 << (ep))) +static inline void usb_settoggle(struct usbdev *dev, + unsigned int ep, + unsigned int out, + int bit) +{ + dev->toggle2[out] &= ~(1 << ep); + dev->toggle2[out] |= bit << ep; +} + + +/* Endpoint halt control/status */ +#define usb_endpoint_out(ep_dir) (((ep_dir >> 7) & 1) ^ 1) +#define usb_endpoint_halt(dev, ep, out) ((dev)->halted[out] |= (1 << (ep))) +#define usb_endpoint_running(dev, ep, out) ((dev)->halted[out] &= ~(1 << (ep))) +#define usb_endpoint_halted(dev, ep, out) ((dev)->halted[out] & (1 << (ep))) + + +static inline unsigned int __create_pipe(usbdev_t *dev, unsigned int endpoint) +{ + return (dev->address << 8) | (endpoint << 15) | + ((dev->lowspeed == 1) << 26); +} + +static inline unsigned int __default_pipe(struct usbdev *dev) +{ + return ((dev->lowspeed == 1) << 26); +} + +/* Create various pipes... */ +#define usb_sndctrlpipe(dev,endpoint) ((PIPE_CONTROL << 30) | __create_pipe(dev,endpoint)) +#define usb_rcvctrlpipe(dev,endpoint) ((PIPE_CONTROL << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN) +#if 0 +#define usb_sndisocpipe(dev,endpoint) ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev,endpoint)) +#define usb_rcvisocpipe(dev,endpoint) ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN) +#endif +#define usb_sndbulkpipe(dev,endpoint) ((PIPE_BULK << 30) | __create_pipe(dev,endpoint)) +#define usb_rcvbulkpipe(dev,endpoint) ((PIPE_BULK << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN) +#if 0 +#define usb_sndintpipe(dev,endpoint) ((PIPE_INTERRUPT << 30) | __create_pipe(dev,endpoint)) +#define usb_rcvintpipe(dev,endpoint) ((PIPE_INTERRUPT << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN) +#endif +#define usb_snddefctrl(dev) ((PIPE_CONTROL << 30) | __default_pipe(dev)) +#define usb_rcvdefctrl(dev) ((PIPE_CONTROL << 30) | __default_pipe(dev) | USB_DIR_IN) + + +extern int next_usb_dev; +usbdev_t usb_device[MAX_USB_DEV]; + +void init_devices(void); +void hci_init(void); +int hc_init(struct pci_device *dev); +inline int set_address(uchar address); +inline int clear_stall(uchar device, uchar endpoint); +int poll_usb(); +int configure_device(uint32_t port, uchar controller, unsigned int lowspeed); +int usb_bulk_transfer( uchar devnum, uchar ep, unsigned int len, uchar *data); +int usb_control_msg( uchar devnum, uchar request_type, uchar request, unsigned short wValue, unsigned short wIndex, + unsigned short wLength, void *data); + +int usb_control_msg_x(struct usbdev *dev, unsigned int pipe, u8 request, u8 requesttype, + u16 value, u16 index, void *data, u16 size, int timeout, usb_complete_t complete); +int usb_bulk_msg_x(struct usbdev *usb_dev, unsigned int pipe, + void *data, int len, int *actual_length, int timeout, usb_complete_t complete); + +#endif diff --git a/src/filo/usb/usb_scsi_low.c b/src/filo/usb/usb_scsi_low.c new file mode 100644 index 00000000..20afecae --- /dev/null +++ b/src/filo/usb/usb_scsi_low.c @@ -0,0 +1,172 @@ +#ifdef USB_DISK +/******************************************************************************* + * + * + * Copyright 2003 Steven James and + * LinuxLabs http://www.linuxlabs.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. + * + ******************************************************************************/ + +#include +#include +#include +#include + +#define DEBUG_THIS DEBUG_USB +#include + +#define DPRINTF debug + +#define uchar uint8_t + +//#include "debug_x.h" +#include "usb_scsi_low.h" + +int usb_bulk_transfer( uchar devnum, uchar ep, unsigned int len, uchar *data); + +#define SG_DXFER_FROM_DEV -3 +#define SG_DXFER_TO_DEV -2 + +#define REQUEST_SENSE 0x03 + +#define CBW_SIG 0x43425355 + +typedef struct usb_cbw { + unsigned int signature; + unsigned int tag; + unsigned int transfer_len; // this is exclusive of cbw and csw + + uchar res1:7; + uchar direction:1; // 1 = device to host (read) + + uchar lun:4; + uchar res:4; + + uchar cbw_len:5; // the length of the SCSI command + uchar res3:3; + + uchar scsi_cmd[16]; +} __attribute__ ((packed)) usb_cbw_t; + +#define CSW_SIG 0x53425355 + +typedef struct usb_csw { + unsigned int signature; + unsigned int tag; + unsigned int residue; + uchar status; +} __attribute__ ((packed)) usb_csw_t; + + +int scsi_command( uchar device, unsigned char *cmd, int cmd_len, int direction, unsigned char *data, int data_len, char *sense_data, int sense_len) +{ + usb_cbw_t cbw; + usb_csw_t csw; + int ret; + + memset(&cbw,0,sizeof(usb_cbw_t)); + memset(&csw,0,sizeof(usb_csw_t)); + + cbw.signature = CBW_SIG; + cbw.tag = 777; + + memcpy(cbw.scsi_cmd, cmd, cmd_len); + cbw.cbw_len = cmd_len; + + if(direction == SG_DXFER_FROM_DEV) + cbw.direction=1; + + cbw.transfer_len = data_len; + + ret = usb_bulk_transfer(device, 2, sizeof(cbw), (uchar *) &cbw); + if(ret<0){ + DPRINTF("ERROR:Bulk write:\n"); + } + + if(data_len) { + if(cbw.direction) { + DPRINTF("scsi_command reading %d bytes\n", data_len); + ret = usb_bulk_transfer(device, 0x81, data_len, data); + DPRINTF("scsi_command read %d bytes\n", ret); + if(ret<0 || ret and + * LinuxLabs http://www.linuxlabs.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. + * + ******************************************************************************/ + +#include +#include +#include +#include + +#define DEBUG_THIS DEBUG_USB +#include + +#define DPRINTF debug + +#include "usb.h" +#include "scsi_cmds.h" + +struct usbdisk_info_t { + struct controller *ctrl; + uint16_t heads; + uint16_t cylinders; + uint16_t sectors_per_track; + uint8_t model_number[41]; + uint8_t slave; + sector_t sectors; + int address_mode; +#define ADDRESS_MODE_CHS 0 +#define ADDRESS_MODE_LBA 1 +#define ADDRESS_MODE_LBA48 2 +#define ADDRESS_MODE_PACKET 3 + uint32_t hw_sector_size; + unsigned drive_exists : 1; + unsigned slave_absent : 1; + unsigned removable : 1; + + unsigned char usb_device_address; +}; + +struct usbdisk_info_t usbdisk_info; + +#define TEST 0 + +#if TEST==1 +#include "usb_scsi_low.h" +typedef struct partition_entry { + uchar boot_flag; + + uchar chs[7]; + + unsigned int lba_start; + unsigned int lba_len; +} __attribute__ ((packed)) partition_entry_t; + +typedef struct partition { + char loader[446]; + partition_entry_t entry[4]; + char sig[2]; +} __attribute__ ((packed)) partition_t; +#endif + +int usb_probe(int drive) +{ + struct usbdisk_info_t *info = &usbdisk_info; +#if TEST==1 + partition_t part; + unsigned char sense_data[32]; +#endif + int i,res; + int error_count=100; + + printf("LinuxLabs USB bootloader\n"); + +// outb( 0x30, 0x70); // reset primary boot +// outb( 0xff, 0x71); + init_devices(); + hci_init(); + + info->usb_device_address = 0; + // find first usb device + + while(error_count && (res = poll_usb())) // keep polling usb until no more devices are enumerated + if(res<0) + if(!--error_count) + printf("There is a USB device, but it won't init! This is a bad thing.\n"); + + for(i=0; i< next_usb_dev ; i++) { + if(usb_device[i].class == 0x08 && usb_device[i].subclass == 0x06 && usb_device[i].protocol == 0x50) { + printf("Found USB block device %d\n", i); + if(drive==0) { + info->usb_device_address = i; + break; + } + drive--; + } + } + + if(info->usb_device_address == 0) return -1; + + UnitReady(info->usb_device_address); + +#if TEST==1 +//Test + printf("Requesting initial sense data\n"); + request_sense( info->usb_device_address, sense_data, 32); + PrintSense(sense_data, 32); + + res = ll_read_block(info->usb_device_address, (uint8_t *)&part, 0, 1); + + printf("ll_read_block returns %d\n", res); + + res=-1; + + debug("part address (phy) = %x, (virt) = %x\n", (uint32_t) virt_to_phys(&part), (uint32_t)&part); + + for(i=0; i<4; i++) { + printf("%d: boot=%02x, start=%08x length=%08x\n",i, part.entry[i].boot_flag, part.entry[i].lba_start, part.entry[i] +.lba_len); + } + + +#endif + + return 0; +} +int usb_read(int drive, sector_t sector, void *buffer) +{ + struct usbdisk_info_t *info = &usbdisk_info; + int result; + int blocknum = sector; + int i; +// printf("sector= %d\t", blocknum); + result = ll_read_block(info->usb_device_address, buffer,blocknum, 1); +#if 0 + for(i=0;i<128;i++) { + if((i%4)==0) printf("\n %08x:",i*4); + printf(" %08x ",(uint32_t)*((uint32_t *)buffer+i)); + } +#endif + + if(result!=512) return -1; + + return 0; +} +#endif diff --git a/src/firmware/linuxbios/linuxbios.c b/src/firmware/linuxbios/linuxbios.c new file mode 100644 index 00000000..0ad66dd5 --- /dev/null +++ b/src/firmware/linuxbios/linuxbios.c @@ -0,0 +1,386 @@ +#ifdef LINUXBIOS + +#include "etherboot.h" +#include "dev.h" +#include "linuxbios_tables.h" + +struct meminfo meminfo; +static int lb_failsafe = 1; +static unsigned lb_boot[MAX_BOOT_ENTRIES]; +static unsigned lb_boot_index; +static struct cmos_entries lb_countdown; +static struct cmos_checksum lb_checksum; + +#undef DEBUG_LINUXBIOS + +static void set_base_mem_k(struct meminfo *info, unsigned mem_k) +{ + if ((mem_k <= 640) && (info->basememsize <= mem_k)) { + info->basememsize = mem_k; + } +} +static void set_high_mem_k(struct meminfo *info, unsigned mem_k) +{ + /* Shave off a megabyte before playing */ + if (mem_k < 1024) { + return; + } + mem_k -= 1024; + if (info->memsize <= mem_k) { + info->memsize = mem_k; + } +} + +#define for_each_lbrec(head, rec) \ + for(rec = (struct lb_record *)(((char *)head) + sizeof(*head)); \ + (((char *)rec) < (((char *)head) + sizeof(*head) + head->table_bytes)) && \ + (rec->size >= 1) && \ + ((((char *)rec) + rec->size) <= (((char *)head) + sizeof(*head) + head->table_bytes)); \ + rec = (struct lb_record *)(((char *)rec) + rec->size)) + + +#define for_each_crec(tbl, rec) \ + for(rec = (struct lb_record *)(((char *)tbl) + tbl->header_length); \ + (((char *)rec) < (((char *)tbl) + tbl->size)) && \ + (rec->size >= 1) && \ + ((((char *)rec) + rec->size) <= (((char *)tbl) + tbl->size)); \ + rec = (struct lb_record *)(((char *)rec) + rec->size)) + + + +static void read_lb_memory( + struct meminfo *info, struct lb_memory *mem) +{ + int i; + int entries; + entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]); + for(i = 0; (i < entries); i++) { + if (info->map_count < E820MAX) { + info->map[info->map_count].addr = mem->map[i].start; + info->map[info->map_count].size = mem->map[i].size; + info->map[info->map_count].type = mem->map[i].type; + info->map_count++; + } + switch(mem->map[i].type) { + case LB_MEM_RAM: + { + unsigned long long end; + unsigned long mem_k; + end = mem->map[i].start + mem->map[i].size; +#if defined(DEBUG_LINUXBIOS) + printf("lb: %X%X - %X%X (ram)\n", + (unsigned long)(mem->map[i].start >>32), + (unsigned long)(mem->map[i].start & 0xFFFFFFFF), + (unsigned long)(end >> 32), + (unsigned long)(end & 0xFFFFFFFF)); +#endif /* DEBUG_LINUXBIOS */ + end >>= 10; + mem_k = end; + if (end & 0xFFFFFFFF00000000ULL) { + mem_k = 0xFFFFFFFF; + } + set_base_mem_k(info, mem_k); + set_high_mem_k(info, mem_k); + break; + } + case LB_MEM_RESERVED: + default: +#if defined(DEBUG_LINUXBIOS) + { + unsigned long long end; + end = mem->map[i].start + mem->map[i].size; + printf("lb: %X%X - %X%X (reserved)\n", + (unsigned long)(mem->map[i].start >>32), + (unsigned long)(mem->map[i].start & 0xFFFFFFFF), + (unsigned long)(end >> 32), + (unsigned long)(end & 0xFFFFFFFF)); + } +#endif /* DEBUG_LINUXBIOS */ + break; + } + } +} + +static unsigned cmos_read(unsigned offset, unsigned int size) +{ + unsigned addr, old_addr; + unsigned value; + + addr = offset/8; + + old_addr = inb(0x70); + outb(addr | (old_addr &0x80), 0x70); + value = inb(0x71); + outb(old_addr, 0x70); + + value >>= offset & 0x7; + value &= ((1 << size) - 1); + + return value; +} + +static unsigned cmos_read_checksum(void) +{ + unsigned sum = + (cmos_read(lb_checksum.location, 8) << 8) | + cmos_read(lb_checksum.location +8, 8); + return sum & 0xffff; +} + +static int cmos_valid(void) +{ + unsigned i; + unsigned sum, old_sum; + sum = 0; + if ((lb_checksum.tag != LB_TAG_OPTION_CHECKSUM) || + (lb_checksum.type != CHECKSUM_PCBIOS) || + (lb_checksum.size != sizeof(lb_checksum))) { + return 0; + } + for(i = lb_checksum.range_start; i <= lb_checksum.range_end; i+= 8) { + sum += cmos_read(i, 8); + } + sum = (~sum)&0x0ffff; + old_sum = cmos_read_checksum(); + return sum == old_sum; +} + +static void cmos_write(unsigned offset, unsigned int size, unsigned setting) +{ + unsigned addr, old_addr; + unsigned value, mask, shift; + unsigned sum; + + addr = offset/8; + + shift = offset & 0x7; + mask = ((1 << size) - 1) << shift; + setting = (setting << shift) & mask; + + old_addr = inb(0x70); + sum = cmos_read_checksum(); + sum = (~sum) & 0xffff; + + outb(addr | (old_addr &0x80), 0x70); + value = inb(0x71); + sum -= value; + value &= ~mask; + value |= setting; + sum += value; + outb(value, 0x71); + + sum = (~sum) & 0x0ffff; + outb((lb_checksum.location/8) | (old_addr & 0x80), 0x70); + outb((sum >> 8) & 0xff, 0x71); + outb(((lb_checksum.location +8)/8) | (old_addr & 0x80), 0x70); + outb(sum & 0xff, 0x71); + + outb(old_addr, 0x70); + + return; +} + +static void read_linuxbios_values(struct meminfo *info, + struct lb_header *head) +{ + /* Read linuxbios tables... */ + struct lb_record *rec; + memset(lb_boot, 0, sizeof(lb_boot)); + for_each_lbrec(head, rec) { + switch(rec->tag) { + case LB_TAG_MEMORY: + { + struct lb_memory *mem; + mem = (struct lb_memory *) rec; + read_lb_memory(info, mem); + break; + } + case LB_TAG_CMOS_OPTION_TABLE: + { + struct cmos_option_table *tbl; + struct lb_record *crec; + struct cmos_entries *entry; + tbl = (struct cmos_option_table *)rec; + for_each_crec(tbl, crec) { + /* Pick off the checksum entry and keep it */ + if (crec->tag == LB_TAG_OPTION_CHECKSUM) { + memcpy(&lb_checksum, crec, sizeof(lb_checksum)); + continue; + } + if (crec->tag != LB_TAG_OPTION) + continue; + entry = (struct cmos_entries *)crec; + if ((entry->bit < 112) || (entry->bit > 1020)) + continue; + /* See if LinuxBIOS came up in fallback or normal mode */ + if (memcmp(entry->name, "last_boot", 10) == 0) { + lb_failsafe = cmos_read(entry->bit, entry->length) == 0; + continue; + } + /* Find where the boot countdown is */ + if (memcmp(entry->name, "boot_countdown", 15) == 0) { + lb_countdown = *entry; + continue; + } + /* Find the default boot index */ + if (memcmp(entry->name, "boot_index", 11) == 0) { + lb_boot_index = cmos_read(entry->bit, entry->length); + continue; + } + /* Now filter for the boot order options */ + if (entry->length != 4) + continue; + if (entry->config != 'e') + continue; + if (memcmp(entry->name, "boot_first", 11) == 0) { + lb_boot[0] = cmos_read(entry->bit, entry->length); + } + else if (memcmp(entry->name, "boot_second", 12) == 0) { + lb_boot[1] = cmos_read(entry->bit, entry->length); + } + else if (memcmp(entry->name, "boot_third", 11) == 0) { + lb_boot[2] = cmos_read(entry->bit, entry->length); + } + } + break; + } + default: + break; + }; + } +} + + + +static unsigned long count_lb_records(void *start, unsigned long length) +{ + struct lb_record *rec; + void *end; + unsigned long count; + count = 0; + end = ((char *)start) + length; + for(rec = start; ((void *)rec < end) && + ((signed long)rec->size <= (end - (void *)rec)); + rec = (void *)(((char *)rec) + rec->size)) { + count++; + } + return count; +} + +static int find_lb_table(void *start, void *end, struct lb_header **result) +{ + unsigned char *ptr; + + /* For now be stupid.... */ + for(ptr = start; virt_to_phys(ptr) < virt_to_phys(end); ptr += 16) { + struct lb_header *head = (struct lb_header *)ptr; + if ( (head->signature[0] != 'L') || + (head->signature[1] != 'B') || + (head->signature[2] != 'I') || + (head->signature[3] != 'O')) { + continue; + } + if (head->header_bytes != sizeof(*head)) + continue; +#if defined(DEBUG_LINUXBIOS) + printf("Found canidate at: %X\n", virt_to_phys(head)); +#endif + if (ipchksum((uint16_t *)head, sizeof(*head)) != 0) + continue; +#if defined(DEBUG_LINUXBIOS) + printf("header checksum o.k.\n"); +#endif + if (ipchksum((uint16_t *)(ptr + sizeof(*head)), head->table_bytes) != + head->table_checksum) { + continue; + } +#if defined(DEBUG_LINUXBIOS) + printf("table checksum o.k.\n"); +#endif + if (count_lb_records(ptr + sizeof(*head), head->table_bytes) != + head->table_entries) { + continue; + } +#if defined(DEBUG_LINUXBIOS) + printf("record count o.k.\n"); +#endif + *result = head; + return 1; + }; + return 0; +} + +void get_memsizes(void) +{ + struct lb_header *lb_table; + int found; +#if defined(DEBUG_LINUXBIOS) + printf("\nSearching for linuxbios tables...\n"); +#endif /* DEBUG_LINUXBIOS */ + found = 0; + meminfo.basememsize = 0; + meminfo.memsize = 0; + meminfo.map_count = 0; + /* This code is specific to linuxBIOS but could + * concievably be extended to work under a normal bios. + * but size is important... + */ + if (!found) { + found = find_lb_table(phys_to_virt(0x00000), phys_to_virt(0x01000), &lb_table); + } + if (!found) { + found = find_lb_table(phys_to_virt(0xf0000), phys_to_virt(0x100000), &lb_table); + } + if (found) { +#if defined (DEBUG_LINUXBIOS) + printf("Found LinuxBIOS table at: %X\n", virt_to_phys(lb_table)); +#endif + read_linuxbios_values(&meminfo, lb_table); + } + +#if defined(DEBUG_LINUXBIOS) + printf("base_mem_k = %d high_mem_k = %d\n", + meminfo.basememsize, meminfo.memsize); +#endif /* DEBUG_LINUXBIOS */ + +} + +unsigned long get_boot_order(unsigned long order, unsigned *index) +{ + static int again; + static int checksum_valid; + static unsigned boot_count; + int i; + + if (!lb_failsafe && !again) { + /* Decrement the boot countdown the first time through */ + checksum_valid = cmos_valid(); + boot_count = cmos_read(lb_countdown.bit, lb_countdown.length); + if (boot_count > 0) { + cmos_write(lb_countdown.bit, lb_countdown.length, boot_count -1); + } + again = 1; + } + if (lb_failsafe || !checksum_valid) { + /* When LinuxBIOS is in failsafe mode, or there is an + * invalid cmos checksum ignore all cmos options + */ + return order; + } + for(i = 0; i < MAX_BOOT_ENTRIES; i++) { + unsigned long boot; + boot = order >> (i*BOOT_BITS) & BOOT_MASK; + boot = lb_boot[i] & BOOT_TYPE_MASK; + if (boot >= BOOT_NOTHING) { + boot = BOOT_NOTHING; + } + if (boot_count == 0) { + boot |= BOOT_FAILSAFE; + } + order &= ~(BOOT_MASK << (i * BOOT_BITS)); + order |= (boot << (i*BOOT_BITS)); + } + *index = lb_boot_index; + return order; +} +#endif /* LINUXBIOS */ diff --git a/src/firmware/linuxbios/linuxbios_tables.h b/src/firmware/linuxbios/linuxbios_tables.h new file mode 100644 index 00000000..ee215201 --- /dev/null +++ b/src/firmware/linuxbios/linuxbios_tables.h @@ -0,0 +1,183 @@ +#ifndef LINUXBIOS_TABLES_H +#define LINUXBIOS_TABLES_H + +#include "stdint.h" + +/* The linuxbios table information is for conveying information + * from the firmware to the loaded OS image. Primarily this + * is expected to be information that cannot be discovered by + * other means, such as quering the hardware directly. + * + * All of the information should be Position Independent Data. + * That is it should be safe to relocated any of the information + * without it's meaning/correctnes changing. For table that + * can reasonably be used on multiple architectures the data + * size should be fixed. This should ease the transition between + * 32 bit and 64 bit architectures etc. + * + * The completeness test for the information in this table is: + * - Can all of the hardware be detected? + * - Are the per motherboard constants available? + * - Is there enough to allow a kernel to run that was written before + * a particular motherboard is constructed? (Assuming the kernel + * has drivers for all of the hardware but it does not have + * assumptions on how the hardware is connected together). + * + * With this test it should be straight forward to determine if a + * table entry is required or not. This should remove much of the + * long term compatibility burden as table entries which are + * irrelevant or have been replaced by better alternatives may be + * dropped. Of course it is polite and expidite to include extra + * table entries and be backwards compatible, but it is not required. + */ + + +struct lb_header +{ + uint8_t signature[4]; /* LBIO */ + uint32_t header_bytes; + uint32_t header_checksum; + uint32_t table_bytes; + uint32_t table_checksum; + uint32_t table_entries; +}; + +/* Every entry in the boot enviroment list will correspond to a boot + * info record. Encoding both type and size. The type is obviously + * so you can tell what it is. The size allows you to skip that + * boot enviroment record if you don't know what it easy. This allows + * forward compatibility with records not yet defined. + */ +struct lb_record { + uint32_t tag; /* tag ID */ + uint32_t size; /* size of record (in bytes) */ +}; + +#define LB_TAG_UNUSED 0x0000 + +#define LB_TAG_MEMORY 0x0001 + +struct lb_memory_range { + uint64_t start; + uint64_t size; + uint32_t type; +#define LB_MEM_RAM 1 /* Memory anyone can use */ +#define LB_MEM_RESERVED 2 /* Don't use this memory region */ +#define LB_MEM_TABLE 16 /* Ram configuration tables are kept in */ + +}; + +struct lb_memory { + uint32_t tag; + uint32_t size; + struct lb_memory_range map[0]; +}; + +#define LB_TAG_HWRPB 0x0002 +struct lb_hwrpb { + uint32_t tag; + uint32_t size; + uint64_t hwrpb; +}; + +#define LB_TAG_MAINBOARD 0x0003 +struct lb_mainboard { + uint32_t tag; + uint32_t size; + uint8_t vendor_idx; + uint8_t part_number_idx; + uint8_t strings[0]; +}; + +#define LB_TAG_VERSION 0x0004 +#define LB_TAG_EXTRA_VERSION 0x0005 +#define LB_TAG_BUILD 0x0006 +#define LB_TAG_COMPILE_TIME 0x0007 +#define LB_TAG_COMPILE_BY 0x0008 +#define LB_TAG_COMPILE_HOST 0x0009 +#define LB_TAG_COMPILE_DOMAIN 0x000a +#define LB_TAG_COMPILER 0x000b +#define LB_TAG_LINKER 0x000c +#define LB_TAG_ASSEMBLER 0x000d +struct lb_string { + uint32_t tag; + uint32_t size; + uint8_t string[0]; +}; + +/* The following structures are for the cmos definitions table */ +#define LB_TAG_CMOS_OPTION_TABLE 200 +/* cmos header record */ +struct cmos_option_table { + uint32_t tag; /* CMOS definitions table type */ + uint32_t size; /* size of the entire table */ + uint32_t header_length; /* length of header */ +}; + +/* cmos entry record + This record is variable length. The name field may be + shorter than CMOS_MAX_NAME_LENGTH. The entry may start + anywhere in the byte, but can not span bytes unless it + starts at the beginning of the byte and the length is + fills complete bytes. +*/ +#define LB_TAG_OPTION 201 +struct cmos_entries { + uint32_t tag; /* entry type */ + uint32_t size; /* length of this record */ + uint32_t bit; /* starting bit from start of image */ + uint32_t length; /* length of field in bits */ + uint32_t config; /* e=enumeration, h=hex, r=reserved */ + uint32_t config_id; /* a number linking to an enumeration record */ +#define CMOS_MAX_NAME_LENGTH 32 + uint8_t name[CMOS_MAX_NAME_LENGTH]; /* name of entry in ascii, + variable length int aligned */ +}; + + +/* cmos enumerations record + This record is variable length. The text field may be + shorter than CMOS_MAX_TEXT_LENGTH. +*/ +#define LB_TAG_OPTION_ENUM 202 +struct cmos_enums { + uint32_t tag; /* enumeration type */ + uint32_t size; /* length of this record */ + uint32_t config_id; /* a number identifying the config id */ + uint32_t value; /* the value associated with the text */ +#define CMOS_MAX_TEXT_LENGTH 32 + uint8_t text[CMOS_MAX_TEXT_LENGTH]; /* enum description in ascii, + variable length int aligned */ +}; + +/* cmos defaults record + This record contains default settings for the cmos ram. +*/ +#define LB_TAG_OPTION_DEFAULTS 203 +struct cmos_defaults { + uint32_t tag; /* default type */ + uint32_t size; /* length of this record */ + uint32_t name_length; /* length of the following name field */ + uint8_t name[CMOS_MAX_NAME_LENGTH]; /* name identifying the default */ +#define CMOS_IMAGE_BUFFER_SIZE 128 + uint8_t default_set[CMOS_IMAGE_BUFFER_SIZE]; /* default settings */ +}; + +#define LB_TAG_OPTION_CHECKSUM 204 +struct cmos_checksum { + uint32_t tag; + uint32_t size; + /* In practice everything is byte aligned, but things are measured + * in bits to be consistent. + */ + uint32_t range_start; /* First bit that is checksummed (byte aligned) */ + uint32_t range_end; /* Last bit that is checksummed (byte aligned) */ + uint32_t location; /* First bit of the checksum (byte aligned) */ + uint32_t type; /* Checksum algorithm that is used */ +#define CHECKSUM_NONE 0 +#define CHECKSUM_PCBIOS 1 +}; + + + +#endif /* LINUXBIOS_TABLES_H */ diff --git a/src/genrules.pl b/src/genrules.pl new file mode 100755 index 00000000..0a129c80 --- /dev/null +++ b/src/genrules.pl @@ -0,0 +1,376 @@ +#!/usr/bin/perl -w +# +# Helper program to generate Makefile rules into file Rom from table in +# file NIC +# +# GPL, Ken Yap 2001, with major contributions by Klaus Espenlaub +# Revised 2002 +# + +use strict; + +use bytes; + +use File::Basename; + +use vars qw($familyfile $nic @families $curfam %drivers %pcient %isaent %isalist %buildent $arch @srcs); + +sub __gendep ($$$) +{ + my ($file, $deps, $driver_dep) = @_; + foreach my $source (@$deps) { + my $inc; + my @collect_dep = (); + $inc = "arch/$arch/include/$source" unless ! -e "arch/$arch/include/$source"; + $inc = "include/$source" unless ! -e "include/$source"; + $inc = dirname($file) . "/$source" unless ! -e dirname($file) . "/$source"; + unless (defined($inc)) { + print STDERR "$source from $file not found (shouldn't happen)\n"; + next; + }; + next if (exists ${$driver_dep}{$inc}); + ${$driver_dep}{$inc} = $inc; +# Warn about failure to open, then skip, rather than soldiering on with the read + unless (open(INFILE, "$inc")) { + print STDERR "$inc: $! (shouldn't happen)\n"; + next; + }; + while () { + chomp($_); +# This code is not very smart: no C comments or CPP conditionals processing is +# done. This may cause unexpected (or incorrect) additional dependencies. +# However, ignoring the CPP conditionals is in some sense correct: we need to +# figure out a superset of all the headers for the driver source. + next unless (s/^\s*#include\s*"([^"]*)".*$/$1/); +# Ignore system includes, like the ones in osdep.h + next if ($_ =~ m:^/:); +# Ignore "special" includes, like .buildserial.h + next if /^\./; + push(@collect_dep, $_); + } + close(INFILE); + if (@collect_dep) { + &__gendep($inc, \@collect_dep, $driver_dep); + } + } +} + +sub gendep ($) { + my ($driver) = @_; + + # Automatically generate the dependencies for the driver sources. + my %driver_dep = (); + __gendep( "", [ $driver ], \%driver_dep); + return sort values %driver_dep +} + +# Make sure that every rom name exists only once. +# make will warn if it finds duplicate rules, but it is better to stop +sub checkduplicate (\%$$) { + my ($anyent, $curfam, $romname) = @_; + foreach my $family (@families) { + if (exists($$anyent{$family})) { + my $aref = $$anyent{$family}; + foreach my $entry (@$aref) { + if ($entry->[0] eq $romname) { + print STDERR "\nROM name $romname defined twice. Please correct.\n"; + exit 1; + } + } + } + } +} + +sub genroms($) { + my ($driver) = @_; + + # Automatically discover the ROMS this driver can produce. + unless (open(INFILE, "$driver")) { + print STDERR "$driver: %! (shouldn't happen)\n"; + next; + }; + while () { + chomp($_); + if ($_ =~ m/^\s*PCI_ROM\(\s*0x([0-9A-Fa-f]*)\s*,\s*0x([0-9A-Fa-f]*)\s*,\s*"([^"]*)"\s*,\s*"([^"]*)"\)/) { + + # We store a list of PCI IDs and comments for each PC target + my ($vendor_id, $device_id, $rom, $comment) = (hex($1), hex($2), $3, $4); + my $ids = sprintf("0x%04x,0x%04x", $vendor_id, $device_id); + checkduplicate(%pcient, $curfam, $rom); + push(@{$pcient{$curfam}}, [$rom, $ids, $comment]); + } + elsif($_ =~ m/^\s*ISA_ROM\(\s*"([^"]*)"\s*,\s*"([^"]*)"\)/) { + my ($rom, $comment) = ($1, $2); + # We store the base driver file for each ISA target + $isalist{$rom} = $curfam; + $buildent{$rom} = 1; + checkduplicate(%isaent, $curfam, $rom); + push(@{$isaent{$curfam}}, [$rom, $comment]); + } + elsif($_ =~ m/^\s*PCI_ROM/ or $_ =~ m/^\s*ISA_ROM/) { + # Complain since we cannot parse this. Of course it would be nicer if we could parse... + print STDERR "\nFound incomplete PCI_ROM or ISA_ROM macro in file $driver.\n"; + print STDERR "ROM macros spanning more than one line are not supported,\n"; + print STDERR "please adjust $driver accordingly.\n\n\n"; + exit 1; + } + } +} + +sub addfam ($) { + my ($family) = @_; + + push(@families, $family); + # We store the list of dependencies in the hash for each family + my @deps = &gendep("$family.c"); + $drivers{$family} = join(' ', @deps); + $pcient{$family} = []; + genroms("$family.c"); +} + +sub addrom ($) { + my ($rom, $ids, $comment) = split(' ', $_[0], 3); + + # defaults if missing + $ids = '-' unless ($ids); + $comment = $rom unless ($comment); + if ($ids ne '-') { + # We store a list of PCI IDs and comments for each PCI target + checkduplicate(%pcient, $curfam, $rom); + push(@{$pcient{$curfam}}, [$rom, $ids, $comment]); + } else { + # We store the base driver file for each ISA target + $isalist{$rom} = $curfam; + $buildent{$rom} = 1; + checkduplicate(%isaent, $curfam, $rom); + push(@{$isaent{$curfam}}, [$rom, $comment]); + } +} + +# Return true if this driver is ISA only +sub isaonly ($) { + my $aref = $pcient{$_[0]}; + + return ($#$aref < 0); +} + +$#ARGV >= 1 or die "Usage: $0 Families bin/NIC arch sources...\n"; +$familyfile = shift(@ARGV); +$nic = shift(@ARGV); +$arch = shift(@ARGV); +@srcs = @ARGV; +open FAM, "<$familyfile" or die "Could not open $familyfile: $!\n"; + +$curfam = ''; +while ( ) { + chomp($_); + next if (/^\s*(#.*)?$/); + my ($keyword) = split(' ', $_ , 2); + if ($keyword eq 'family') { + my ($keyword, $driver) = split(' ', $_, 2); + $curfam = ''; + if (! -e "$driver.c") { + print STDERR "Driver file $driver.c not found, skipping...\n"; + next; + } + if ($driver =~ "^arch" && $driver !~ "^arch/$arch") { +# This warning just makes noise for most compilations. +# print STDERR "Driver file $driver.c not for arch $arch, skipping...\n"; + next; + } + &addfam($curfam = $driver); + } else { + # skip until we have a valid family + next if ($curfam eq ''); + &addrom($_); + } +} +close FAM; + +open(N,">$nic") or die "$nic: $!\n"; +print N <[0]; + my $ids = $entry->[1]; + my $comment = $entry->[2]; + print N "$rom\t$ids\t$comment\n"; + } + } + if (exists($isaent{$family})) { + my $aref = $isaent{$family}; + foreach my $entry (@$aref) { + my $rom = $entry->[0]; + my $comment = $entry->[1]; + print N "$rom\t-\t$comment\n"; + } + } + print N "\n"; +} +close(N); + +# Generate the normal source dependencies +print "# Core object file dependencies\n"; +foreach my $source (@srcs) { + next if ($source !~ '[.][cS]$'); + my @deps = &gendep($source); + my $obj = $source; + $obj =~ s/^.*?([^\/]+)\.[cS]/bin\/$1.o/; + foreach my $dep (@deps) { + print "$obj: $dep\n"; + } + print("\n"); +} + +# Generate the assignments to DOBJS and BINS +print "# Driver object files and ROM image files\n"; +print "DOBJS\t:=\n"; +print "PCIOBJS\t:=\n"; + +print "# Target formats\n"; +print "EB_ISOS\t:=\n"; +print "EB_LISOS\t:=\n"; +print "EB_COMS\t:=\n"; +print "EB_EXES\t:=\n"; +print "EB_LILOS\t:=\n"; +print "EB_ZLILOS\t:=\n"; +print "EB_PXES\t:=\n"; +print "EB_ZPXES\t:=\n"; +print "EB_DSKS\t:=\n"; +print "EB_ZDSKS\t:=\n"; +print "EB_ELFS\t:=\n"; +print "EB_ZELFS\t:=\n"; +print "EB_LMELFS\t:=\n"; +print "EB_ZLMELFS\t:=\n"; +print "EB_ELFDS\t:=\n"; +print "EB_ZELFDS\t:=\n"; +print "EB_LMELFDS\t:=\n"; +print "EB_ZLMELFDS\t:=\n"; + +foreach my $pci (sort keys %pcient) { + my $img = basename($pci); + + print "DOBJS\t+= \$(BIN)/$img.o\n"; + print "PCIOBJS\t+= \$(BIN)/$img.o\n" unless isaonly($pci); + +# Output targets + print "EB_LILOS\t+= \$(BIN)/$img.lilo \nEB_ZLILOS\t+= \$(BIN)/$img.zlilo\n"; + print "EB_PXES\t+= \$(BIN)/$img.pxe \nEB_ZPXES\t+= \$(BIN)/$img.zpxe\n"; + print "EB_DSKS\t+= \$(BIN)/$img.dsk \nEB_ZDSKS\t+= \$(BIN)/$img.zdsk\n"; + print "EB_ELFS\t+= \$(BIN)/$img.elf \nEB_ZELFS\t+= \$(BIN)/$img.zelf\n"; + print "EB_LMELFS\t+= \$(BIN)/$img.lmelf \nEB_ZLMELFS\t+= \$(BIN)/$img.zlmelf\n"; + print "EB_ELFDS\t+= \$(BIN)/$img.elfd \nEB_ZELFDS\t+= \$(BIN)/$img.zelfd\n"; + print "EB_LMELFDS\t+= \$(BIN)/$img.lmelfd \nEB_ZLMELFDS\t+= \$(BIN)/$img.zlmelfd\n"; + print "EB_BIMAGES\t+= \$(BIN)/$img.bImage \nEB_BZIMAGES\t+= \$(BIN)/$img.bzImage\n"; + print "EB_ISOS\t+= \$(BIN)/$img.iso\n"; + print "EB_LISOS\t+= \$(BIN)/$img.liso\n"; + print "EB_COMS\t+= \$(BIN)/$img.com\n"; + print "EB_EXES\t+= \$(BIN)/$img.exe\n"; +} + +foreach my $img (sort keys %buildent) { + + print "DOBJS\t+= \$(BIN)/$img.o\n"; + +# Output targets + print "EB_LILOS\t+= \$(BIN)/$img.lilo \nEB_ZLILOS\t+= \$(BIN)/$img.zlilo\n"; + print "EB_PXES\t+= \$(BIN)/$img.pxe \nEB_ZPXES\t+= \$(BIN)/$img.zpxe\n"; + print "EB_DSKS\t+= \$(BIN)/$img.dsk \nEB_ZDSKS\t+= \$(BIN)/$img.zdsk\n"; + print "EB_ELFS\t+= \$(BIN)/$img.elf \nEB_ZELFS\t+= \$(BIN)/$img.zelf\n"; + print "EB_LMELFS\t+= \$(BIN)/$img.lmelf \nEB_ZLMELFS\t+= \$(BIN)/$img.zlmelf\n"; + print "EB_ELFDS\t+= \$(BIN)/$img.elfd \nEB_ZELFDS\t+= \$(BIN)/$img.zelfd\n"; + print "EB_LMELFDS\t+= \$(BIN)/$img.lmelfd \nEB_ZLMELFDS\t+= \$(BIN)/$img.zlmelfd\n"; + print "EB_BIMAGES\t+= \$(BIN)/$img.bImage \nEB_BZIMAGE\t+= \$(BIN)/$img.bzImage\n"; + print "EB_ISOS\t+= \$(BIN)/$img.iso\n"; + print "EB_LISOS\t+= \$(BIN)/$img.liso\n"; + print "EB_COMS\t+= \$(BIN)/$img.com\n"; + print "EB_EXES\t+= \$(BIN)/$img.exe\n"; +} + +print "ROMS\t:=\n"; +foreach my $family (sort keys %pcient) { + my $aref = $pcient{$family}; + foreach my $entry (@$aref) { + my $rom = $entry->[0]; + print "ROMS\t+= \$(BIN)/$rom.rom \$(BIN)/$rom.zrom\n"; + } +} +foreach my $isa (sort keys %isalist) { + print "ROMS\t+= \$(BIN)/$isa.rom \$(BIN)/$isa.zrom\n"; +} + +# Generate the *.o rules +print "\n# Rules to build the driver object files\n"; +foreach my $pci (sort keys %drivers) { + # For ISA the rule for .o will generated later + next if isaonly($pci); + # PCI drivers are compiled only once for all ROMs + (my $macro = basename($pci)) =~ tr/\-/_/; + my $obj = basename($pci); + my $deps = $drivers{$pci}; + print < \$@ + \$(MAKEROM) \$(MAKEROM_FLAGS) \$(MAKEROM_\$(ROMCARD)) \$(MAKEROM_ID_\$(ROMCARD)) -i\$(IDENT) \$@ + +EOF + } +} + +# ISA ROMs are prepared from the matching code images +# Think this can probably be removed, but not sure +foreach my $isa (sort keys %isalist) { + print < + */ + +extern void btext_clearscreen(void); + +extern boot_infos_t disp_bi; +extern u32 boot_text_mapped; + +void btext_setup_display(u32 width, u32 height, u32 depth, u32 pitch, + unsigned long address); +void map_boot_text(void); + +void btext_drawchar(char c); +void btext_drawstring(const char *str); +void btext_drawhex(u32 v); + +void btext_putc(int c); + +void btext_init(void); + +#endif /* _BTEXT_H */ diff --git a/src/include/byteswap.h b/src/include/byteswap.h new file mode 100644 index 00000000..bed14857 --- /dev/null +++ b/src/include/byteswap.h @@ -0,0 +1,20 @@ +#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 swap32(x) __bswap_32(x) +#define swap16(x) __bswap_16(x) +#define bswap_32(x) __bswap_32(x) +#define bswap_16(x) __bswap_16(x) + +#endif /* ETHERBOOT_BYTESWAP_H */ diff --git a/src/include/callbacks.h b/src/include/callbacks.h new file mode 100644 index 00000000..2a754785 --- /dev/null +++ b/src/include/callbacks.h @@ -0,0 +1,45 @@ +/* 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 , January 2004. + * + * $Id$ + */ + +#ifndef CALLBACKS_H +#define CALLBACKS_H + +/* Opcodes and flags for in_call() + */ +#define EB_OPCODE(x) ( (x) & 0xffff ) +#define EB_OPCODE_MAIN (0x0000) +#define EB_OPCODE_CHECK (0x6948) /* 'Hi' */ +#define EB_OPCODE_PXE (0x7850) /* 'Px' */ +#define EB_OPCODE_PXENV (0x7650) /* 'Pv' */ +#define EB_USE_INTERNAL_STACK ( 1 << 16 ) +#define EB_CALL_FROM_REAL_MODE ( 1 << 17 ) /* i386 only */ +#define EB_SKIP_OPCODE ( 1 << 18 ) + +/* Standard return codes + */ +#define EB_CHECK_RESULT (0x6f486948) /* 'HiHo' */ + +/* Include arch-specific callbacks bits + */ +#include "callbacks_arch.h" + +/* Skip the definitions that won't make sense to the assembler */ +#ifndef ASSEMBLY + +#include + +#ifndef in_call_data_t +typedef struct {} empty_struct_t; +#define in_call_data_t empty_struct_t +#endif + +#endif /* ASSEMBLY */ + +#endif /* CALLBACKS_H */ diff --git a/src/include/coff.h b/src/include/coff.h new file mode 100644 index 00000000..a73fda52 --- /dev/null +++ b/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/src/include/cpu.h b/src/include/cpu.h new file mode 100644 index 00000000..b2c428f7 --- /dev/null +++ b/src/include/cpu.h @@ -0,0 +1,6 @@ +#ifndef CPU_H +#define CPU_H + +#include "bits/cpu.h" + +#endif /* CPU_H */ diff --git a/src/include/debug.h b/src/include/debug.h new file mode 100644 index 00000000..bb5d33f3 --- /dev/null +++ b/src/include/debug.h @@ -0,0 +1,28 @@ +#ifndef DEBUG_H +#define DEBUG_H + +//#include +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/src/include/dev.h b/src/include/dev.h new file mode 100644 index 00000000..bd352eb7 --- /dev/null +++ b/src/include/dev.h @@ -0,0 +1,126 @@ +#ifndef DEV_H +#define DEV_H + +#include "isa.h" +#include "pci.h" + +/* Need to check the packing of this struct if Etherboot is ported */ +struct dev_id +{ + unsigned short vendor_id; + unsigned short device_id; + unsigned char bus_type; +#define PCI_BUS_TYPE 1 +#define ISA_BUS_TYPE 2 +}; + +/* Dont use sizeof, that will include the padding */ +#define DEV_ID_SIZE 8 + + +struct pci_probe_state +{ +#ifdef CONFIG_PCI + struct pci_device dev; + int advance; +#else + int dummy; +#endif +}; +struct isa_probe_state +{ +#ifdef CONFIG_ISA + const struct isa_driver *driver; + int advance; +#else + int dummy; +#endif +}; + +union probe_state +{ + struct pci_probe_state pci; + struct isa_probe_state isa; +}; + +struct dev +{ + void (*disable)P((struct dev *)); + struct dev_id devid; /* device ID string (sent to DHCP server) */ + int index; /* Index of next device on this controller to probe */ + int type; /* Type of device I am probing for */ + int how_probe; /* First, next or awake */ + int to_probe; /* Flavor of device I am probing */ + int failsafe; /* Failsafe probe requested */ + int type_index; /* Index of this device (within type) */ +#define PROBE_NONE 0 +#define PROBE_PCI 1 +#define PROBE_ISA 2 + union probe_state state; +}; + + +#define NIC_DRIVER 0 +#define DISK_DRIVER 1 +#define FLOPPY_DRIVER 2 + +#define BRIDGE_DRIVER 1000 + +#define PROBE_FIRST (-1) +#define PROBE_NEXT 0 +#define PROBE_AWAKE 1 /* After calling disable bring up the same device */ + +/* The probe result codes are selected + * to allow them to be fed back into the probe + * routine and get a successful probe. + */ +#define PROBE_FAILED PROBE_FIRST +#define PROBE_WORKED PROBE_NEXT + +extern int probe(struct dev *dev); +extern void disable(struct dev *dev); + +/* Boot option values + * option & BOOT_TYPE_MASK should equal a driver for probing + */ + +#define BOOT_NIC 0x0 /* Boot from a nic */ +#define BOOT_DISK 0x1 /* Boot from disk */ +#define BOOT_FLOPPY 0x2 /* Boot from a floppy */ + +#define BOOT_NOTHING 0x3 /* Last valid boot choice */ + +/* Do magic failsafe boot processing */ +#define BOOT_FAILSAFE 0x8 + +#define BOOT_BITS 4 +#define BOOT_MASK ((1 << (BOOT_BITS)) - 1) +#define BOOT_TYPE_MASK ((1 << (BOOT_BITS - 1)) - 1) + +#define MAX_BOOT_ENTRIES 3 + +#define BOOT_ALL_VALUE (1<= 512 + */ + unsigned int sectors_per_read; /* The number of 512 byte sectors + * returned by each read call. + * All I/O must be aligned to this size. + */ + unsigned int bytes; /* The number of bytes in the read buffer. */ + sector_t sectors; /* The number of sectors on the drive. */ + sector_t sector; /* The first sector in the driver buffer */ + unsigned char *buffer; /* The data read from the drive */ + void *priv; /* driver can hang private data here */ + + unsigned long disk_offset; + int direction; +}; + +extern struct disk disk; +extern int url_file(const char *name, + int (*fnc)(unsigned char *, unsigned int, unsigned int, int)); + +extern int disk_probe(struct dev *dev); +extern int disk_load_configuration(struct dev *dev); +extern int disk_load(struct dev *dev); +extern void disk_disable(void); + + +#ifndef DOWNLOAD_PROTO_DISK +#define disk_disable() do { } while(0) +#endif + +#define SECTOR_SIZE 512 +#define SECTOR_SHIFT 9 + +/* Maximum block_size that may be set. */ +#define DISK_BUFFER_SIZE (18 * SECTOR_SIZE) + +#endif /* DISK_H */ diff --git a/src/include/dns_resolver.h b/src/include/dns_resolver.h new file mode 100644 index 00000000..b04a9764 --- /dev/null +++ b/src/include/dns_resolver.h @@ -0,0 +1,58 @@ +// dns_resolver.h - #define statements for the DNS resolver + +// We only need A and CNAME queries (later possibly AAAA/A6?) +#define QUERYTYPE_A 1 +#define QUERYTYPE_CNAME 5 + +// We only query with INTERNET class (not CHAOS or whatever) +#define QUERYCLASS_INET 1 + +// Our first query will have the identifier <1> (arbitrary - +// remember however that (256 - QUERYIDENTIFIER)/2 > MAX_CNAME_RECURSION !!! +#define QUERYIDENTIFIER 1 + +// Query flags are standard values here +#define QUERYFLAGS 0x0100 +#define QUERYFLAGS_MASK 0xf8 +#define QUERYFLAGS_WANT 0x80 + +// Indices inside the byte array that holds DNS queries/answers +#define QINDEX_ID 0 +#define QINDEX_FLAGS 2 +#define QINDEX_NUMQUEST 4 +#define QINDEX_NUMANSW 6 +#define QINDEX_NUMAUTH 8 +#define QINDEX_NUMADDIT 10 +#define QINDEX_QUESTION 12 +#define QINDEX_QTYPE 14 +#define QINDEX_QCLASS 16 +#define QINDEX_STORE_A 256 + +// Constant UDP port number for DNS traffic +#define UDP_PORT_DNS 53 + +// Return values that the package parser may give +// This packet was not for us (broadcast or whatever) +#define RET_PACK_GARBAG 0 +// Retrieved an address - query finishes +#define RET_GOT_ADDR 1 +// No A record for that hostname - try running a CNAME query +#define RET_RUN_CNAME_Q 2 +// The CNAME query returned a valid hostname - run A query on that +#define RET_RUN_NEXT_A 3 +// The CNAME query failed - stop resolving +#define RET_CNAME_FAIL 4 +// We have a reliable input that claims that the hostname does not exist +#define RET_NOSUCHNAME 5 +// The name server response is somehow bogus/can not be parsed -> Abort +#define RET_DNSERROR 6 + +// Return values that the query engine may give +// DNS query succeeded, IP address delivered +#define RET_DNS_OK 0 +// DNS query failed +#define RET_DNS_FAIL 1 + +// Error codes the DNS server can send to us +#define ERR_NOSUCHNAME 3 + diff --git a/src/include/elf.h b/src/include/elf.h new file mode 100644 index 00000000..606b4192 --- /dev/null +++ b/src/include/elf.h @@ -0,0 +1,234 @@ +#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 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 +/* + * 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 */ + +#include "elf_boot.h" + +#endif /* ELF_H */ diff --git a/src/include/elf_boot.h b/src/include/elf_boot.h new file mode 100644 index 00000000..878a870a --- /dev/null +++ b/src/include/elf_boot.h @@ -0,0 +1,84 @@ +#ifndef ELF_BOOT_H +#define ELF_BOOT_H + + +/* This defines the structure of a table of parameters useful for ELF + * bootable images. These parameters are all passed and generated + * by the bootloader to the booted image. For simplicity and + * consistency the Elf Note format is reused. + * + * All of the information must be Position Independent Data. + * That is it must be safe to relocate the whole ELF boot parameter + * block without changing the meaning or correctnes of the data. + * Additionally it must be safe to permute the order of the ELF notes + * to any possible permutation without changing the meaning or correctness + * of the data. + * + */ + +#define ELF_BHDR_MAGIC 0x0E1FB007 + +#ifndef ASSEMBLY +#include +typedef uint16_t Elf_Half; +typedef uint32_t Elf_Word; + +typedef struct Elf_Bhdr +{ + Elf_Word b_signature; /* "0x0E1FB007" */ + Elf_Word b_size; + Elf_Half b_checksum; + Elf_Half b_records; +} Elf_Bhdr; + +typedef struct Elf_Nhdr +{ + Elf_Word n_namesz; /* Length of the note's name. */ + Elf_Word n_descsz; /* Length of the note's descriptor. */ + Elf_Word n_type; /* Type of the note. */ +} Elf_Nhdr; + +#endif /* ASSEMBLY */ + +/* Standardized Elf image notes for booting... The name for all of these is ELFBoot */ +#define ELF_NOTE_BOOT "ELFBoot" + +#define EIN_PROGRAM_NAME 0x00000001 +/* The program in this ELF file */ +#define EIN_PROGRAM_VERSION 0x00000002 +/* The version of the program in this ELF file */ +#define EIN_PROGRAM_CHECKSUM 0x00000003 +/* ip style checksum of the memory image. */ + + +/* Notes that are passed to a loaded image */ +/* For standard notes n_namesz must be zero */ +#define EBN_FIRMWARE_TYPE 0x00000001 +/* ASCIZ name of the platform firmware. */ +#define EBN_BOOTLOADER_NAME 0x00000002 +/* This specifies just the ASCIZ name of the bootloader */ +#define EBN_BOOTLOADER_VERSION 0x00000003 +/* This specifies the version of the bootloader as an ASCIZ string */ +#define EBN_COMMAND_LINE 0x00000004 +/* This specifies a command line that can be set by user interaction, + * and is provided as a free form ASCIZ string to the loaded image. + */ +#define EBN_NOP 0x00000005 +/* A note nop note has no meaning, useful for inserting explicit padding */ +#define EBN_LOADED_IMAGE 0x00000006 +/* An ASCIZ string naming the loaded image */ + + +/* Etherboot specific notes */ +#define EB_PARAM_NOTE "Etherboot" +#define EB_IA64_SYSTAB 0x00000001 +#define EB_IA64_MEMMAP 0x00000002 +#define EB_IA64_FPSWA 0x00000003 +#define EB_IA64_CONINFO 0x00000004 +#define EB_BOOTP_DATA 0x00000005 +#define EB_HEADER 0x00000006 +#define EB_IA64_IMAGE_HANDLE 0x00000007 +#define EB_I386_MEMMAP 0x00000008 + + +#endif /* ELF_BOOT_H */ diff --git a/src/include/endian.h b/src/include/endian.h new file mode 100644 index 00000000..32006224 --- /dev/null +++ b/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/src/include/etherboot.h b/src/include/etherboot.h new file mode 100644 index 00000000..3d01fb9e --- /dev/null +++ b/src/include/etherboot.h @@ -0,0 +1,459 @@ +#ifndef ETHERBOOT_H +#define ETHERBOOT_H + +#include +#include "osdep.h" + +#ifndef BOOT_FIRST +#define BOOT_FIRST BOOT_NIC +#endif +#ifndef BOOT_SECOND +#define BOOT_SECOND BOOT_NOTHING +#endif +#ifndef BOOT_THIRD +#define BOOT_THIRD BOOT_NOTHING +#endif + +#define DEFAULT_BOOT_ORDER ( \ + (BOOT_FIRST << (0*BOOT_BITS)) | \ + (BOOT_SECOND << (1*BOOT_BITS)) | \ + (BOOT_THIRD << (2*BOOT_BITS)) | \ + (BOOT_NOTHING << (3*BOOT_BITS)) | \ + 0) + +#ifdef BOOT_INDEX +#define DEFAULT_BOOT_INDEX BOOT_INDEX +#else +#define DEFAULT_BOOT_INDEX 0 +#endif + +#if !defined(TAGGED_IMAGE) && !defined(AOUT_IMAGE) && !defined(ELF_IMAGE) && !defined(ELF64_IMAGE) && !defined(COFF_IMAGE) && !defined(RAW_IMAGE) +#define TAGGED_IMAGE /* choose at least one */ +#endif + +#undef CODE16 +#if defined(PCBIOS) +#define CODE16 +#endif + +#define K_ESC '\033' +#define K_EOF '\04' /* Ctrl-D */ +#define K_INTR '\03' /* Ctrl-C */ + +/* Edit this to change the path to hostspecific kernel image + kernel. in RARP boot */ +#ifndef DEFAULT_KERNELPATH +#define DEFAULT_KERNELPATH "/tftpboot/kernel.%@" +#endif + +#ifdef FREEBSD_PXEEMU +#undef DEFAULT_BOOTFILE +#ifndef PXENFSROOTPATH +#define PXENFSROOTPATH "" +#endif +#define DEFAULT_BOOTFILE PXENFSROOTPATH "/boot/pxeboot" +#endif + +/* Clean up console settings... mainly CONSOLE_FIRMWARE and CONSOLE_SERIAL are used + * in the sources (except start.S and serial.S which cannot include + * etherboot.h). At least one of the CONSOLE_xxx has to be set, and + * CONSOLE_DUAL sets both CONSOLE_CRT and CONSOLE_SERIAL. If none is set, + * CONSOLE_CRT is assumed. */ +#ifdef CONSOLE_CRT +#define CONSOLE_FIRMWARE +#endif +#ifdef CONSOLE_DUAL +#undef CONSOLE_FIRMWARE +#define CONSOLE_FIRMWARE +#undef CONSOLE_SERIAL +#define CONSOLE_SERIAL +#endif +#if defined(CONSOLE_FIRMWARE) && defined(CONSOLE_SERIAL) +#undef CONSOLE_DUAL +#define CONSOLE_DUAL +#endif +#if !defined(CONSOLE_FIRMWARE) && !defined(CONSOLE_SERIAL) +#define CONSOLE_FIRMWARE +#endif + +#if !defined(DOWNLOAD_PROTO_TFTP) && !defined(DOWNLOAD_PROTO_NFS) && !defined(DOWNLOAD_PROTO_SLAM) && !defined(DOWNLOAD_PROTO_TFTM) && !defined(DOWNLOAD_PROTO_DISK) && !defined(DOWNLOAD_PROTO_HTTP) +#error No download protocol defined! +#endif + +#ifndef MAX_TFTP_RETRIES +#define MAX_TFTP_RETRIES 20 +#endif + +#ifndef MAX_BOOTP_RETRIES +#define MAX_BOOTP_RETRIES 20 +#endif + +#define MAX_BOOTP_EXTLEN (ETH_MAX_MTU-sizeof(struct bootpip_t)) + +#ifndef MAX_ARP_RETRIES +#define MAX_ARP_RETRIES 20 +#endif + +#ifndef MAX_RPC_RETRIES +#define MAX_RPC_RETRIES 20 +#endif + +/* Link configuration time in tenths of a second */ +#ifndef VALID_LINK_TIMEOUT +#define VALID_LINK_TIMEOUT 100 /* 10.0 seconds */ +#endif + +/* Inter-packet retry in ticks */ +#ifndef TIMEOUT +#define TIMEOUT (10*TICKS_PER_SEC) +#endif + +#ifndef BOOTP_TIMEOUT +#define BOOTP_TIMEOUT (2*TICKS_PER_SEC) +#endif + +/* Max interval between IGMP packets */ +#define IGMP_INTERVAL (10*TICKS_PER_SEC) +#define IGMPv1_ROUTER_PRESENT_TIMEOUT (400*TICKS_PER_SEC) + +/* These settings have sense only if compiled with -DCONGESTED */ +/* total retransmission timeout in ticks */ +#define TFTP_TIMEOUT (30*TICKS_PER_SEC) +/* packet retransmission timeout in ticks */ +#ifdef CONGESTED +#define TFTP_REXMT (3*TICKS_PER_SEC) +#else +#define TFTP_REXMT TIMEOUT +#endif + +#ifndef NULL +#define NULL ((void *)0) +#endif + +#include "if_ether.h" + +enum { + ARP_CLIENT, ARP_SERVER, ARP_GATEWAY, +#ifdef DNS_RESOLVER + ARP_NAMESERVER, +#endif +#ifdef PXE_EXPORT + ARP_PROXYDHCP, +#endif + MAX_ARP +}; + +#define IGMP_SERVER 0 +#define MAX_IGMP IGMP_SERVER+1 + +#define RARP_REQUEST 3 +#define RARP_REPLY 4 + +#include "in.h" + +#define MULTICAST_MASK 0xF0000000 +#define MULTICAST_NETWORK 0xE0000000 + +/* Helper macros used to identify when DHCP options are valid/invalid in/outside of encapsulation */ +#define NON_ENCAP_OPT in_encapsulated_options == 0 && +#ifdef ALLOW_ONLY_ENCAPSULATED +#define ENCAP_OPT in_encapsulated_options == 1 && +#else +#define ENCAP_OPT +#endif + +#include "if_arp.h" +#include "ip.h" +#include "udp.h" +#include "tcp.h" +#include "bootp.h" +#include "tftp.h" +#include "igmp.h" +#include "nfs.h" + +struct arptable_t { + in_addr ipaddr; + uint8_t node[6]; +} PACKED; + +struct igmptable_t { + in_addr group; + unsigned long time; +} PACKED; + +#define KERNEL_BUF (BOOTP_DATA_ADDR->bootp_reply.bp_file) + +#define FLOPPY_BOOT_LOCATION 0x7c00 + +struct rom_info { + unsigned short rom_segment; + unsigned short rom_length; +}; + +extern inline int rom_address_ok(struct rom_info *rom, int assigned_rom_segment) +{ + return (assigned_rom_segment < 0xC000 + || assigned_rom_segment == rom->rom_segment); +} + +/* Define a type for passing info to a loaded program */ +struct ebinfo { + uint8_t major, minor; /* Version */ + uint16_t flags; /* Bit flags */ +}; + +/*************************************************************************** +External prototypes +***************************************************************************/ +/* main.c */ +struct Elf_Bhdr; +extern int in_call(in_call_data_t *data, uint32_t opcode, va_list params); +extern void console_init(void); +extern int main(in_call_data_t *data, va_list params); +extern int loadkernel P((const char *fname)); +extern char as_main_program; +/* nic.c */ +extern void rx_qdrain P((void)); +extern int tftp P((const char *name, int (*)(unsigned char *, unsigned int, unsigned int, int))); +extern int tftp_block P((struct tftpreq_info_t *, struct tftpblk_info_t *)); +extern int ip_transmit P((int len, const void *buf)); +extern void build_ip_hdr P((unsigned long destip, int ttl, int protocol, + int option_len, int len, const void *buf)); +extern void build_udp_hdr P((unsigned long destip, + unsigned int srcsock, unsigned int destsock, int ttl, + int len, const void *buf)); +extern int udp_transmit P((unsigned long destip, unsigned int srcsock, + unsigned int destsock, int len, const void *buf)); +extern int tcp_transmit(unsigned long destip, unsigned int srcsock, + unsigned int destsock, long send_seq, long recv_seq, + int window, int flags, int len, const void *buf); +int tcp_reset(struct iphdr *ip); +typedef int (*reply_t)(int ival, void *ptr, unsigned short ptype, struct iphdr *ip, struct udphdr *udp, struct tcphdr *tcp); +extern int await_reply P((reply_t reply, int ival, void *ptr, long timeout)); +extern int decode_rfc1533 P((unsigned char *, unsigned int, unsigned int, int)); +extern void join_group(int slot, unsigned long group); +extern void leave_group(int slot); +#define RAND_MAX 2147483647L +extern uint16_t ipchksum P((const void *ip, unsigned long len)); +extern uint16_t add_ipchksums P((unsigned long offset, uint16_t sum, uint16_t new)); +extern int32_t random P((void)); +extern long rfc2131_sleep_interval P((long base, int exp)); +extern long rfc1112_sleep_interval P((long base, int exp)); +#ifndef DOWNLOAD_PROTO_TFTP +#define tftp(fname, load_block) 0 +#endif +extern void cleanup P((void)); + +/* nfs.c */ +extern void rpc_init(void); +extern int nfs P((const char *name, int (*)(unsigned char *, unsigned int, unsigned int, int))); +extern void nfs_umountall P((int)); + +/* proto_slam.c */ +extern int url_slam P((const char *name, int (*fnc)(unsigned char *, unsigned int, unsigned int, int))); + +/* proto_tftm.c */ +extern int url_tftm P((const char *name, int (*fnc)(unsigned char *, unsigned int, unsigned int, int))); + +/* config.c */ +extern void print_config(void); + +/* isa_probe.c and pci_probe.c */ +struct dev; +extern void isa_enumerate(void); +extern int isa_probe(struct dev *, const char *); +extern void pci_enumerate(void); +extern int pci_probe(struct dev *, const char *); + +/* heap.c */ +extern void init_heap(void); +extern void *allot(size_t size); +void forget(void *ptr); +extern void *allot2(size_t size, uint32_t mask); +void forget2(void *ptr); +/* Physical address of the heap */ +extern size_t heap_ptr, heap_top, heap_bot; + +/* osloader.c */ +/* Be careful with sector_t it is an unsigned long long on x86 */ +typedef uint64_t sector_t; +typedef sector_t (*os_download_t)(unsigned char *data, unsigned int len, int eof); +extern os_download_t probe_image(unsigned char *data, unsigned int len); +extern int load_block P((unsigned char *, unsigned int, unsigned int, int )); + +/* misc.c */ +extern void twiddle P((void)); +extern void sleep P((int secs)); +extern void interruptible_sleep P((int secs)); +extern void poll_interruptions P((void)); +extern int strcasecmp P((const char *a, const char *b)); +extern char *substr P((const char *a, const char *b)); +extern unsigned long strtoul P((const char *p, const char **, int base)); +extern void printf P((const char *, ...)); +extern int sprintf P((char *, const char *, ...)); +extern int inet_aton P((const char *p, in_addr *i)); +#ifdef PCBIOS +extern void gateA20_set P((void)); +#define gateA20_unset() +#else +#define gateA20_set() +#define gateA20_unset() +#endif +extern void putchar P((int)); +extern int getchar P((void)); +extern int iskey P((void)); + +/* pcbios.S */ +extern int console_getc P((void)); +extern void console_putc P((int)); +extern int console_ischar P((void)); +extern int getshift P((void)); +extern int int15 P((int)); +#ifdef POWERSAVE +extern void cpu_nap P((void)); +#endif /* POWERSAVE */ + +/* basemem.c */ +extern uint32_t get_free_base_memory ( void ); +extern void allot_real_mode_stack ( void ); +extern void forget_real_mode_stack ( void ); +extern void * allot_base_memory ( size_t ); +extern void forget_base_memory ( void*, size_t ); +extern void free_unused_base_memory ( void ); +extern void forget_prefix_base_memory ( void ); +extern void forget_runtime_base_memory ( uint32_t old_addr ); + +struct e820entry { + uint64_t addr; + uint64_t size; + uint32_t type; +#define E820_RAM 1 +#define E820_RESERVED 2 +#define E820_ACPI 3 /* usable as RAM once ACPI tables have been read */ +#define E820_NVS 4 +} PACKED; +#define E820ENTRY_SIZE 20 +#define E820MAX 32 +struct meminfo { + uint16_t basememsize; + uint16_t pad; + uint32_t memsize; + uint32_t map_count; + struct e820entry map[E820MAX]; +} PACKED; +extern struct meminfo meminfo; +extern void get_memsizes(void); +extern unsigned long get_boot_order(unsigned long order, unsigned *index); +#ifndef NORELOCATE +extern void relocate(void); +extern void relocate_to(unsigned long phys_dest); +#else +#define relocate() do {} while(0) +#endif +extern void disk_init P((void)); +extern unsigned int pcbios_disk_read P((int drv,int c,int h,int s,char *buf)); + +/* start32.S */ +struct os_entry_regs { + /* Be careful changing this structure + * as it is used by assembly language code. + */ + uint32_t edi; /* 0 */ + uint32_t esi; /* 4 */ + uint32_t ebp; /* 8 */ + uint32_t esp; /* 12 */ + uint32_t ebx; /* 16 */ + uint32_t edx; /* 20 */ + uint32_t ecx; /* 24 */ + uint32_t eax; /* 28 */ + + uint32_t saved_ebp; /* 32 */ + uint32_t saved_esi; /* 36 */ + uint32_t saved_edi; /* 40 */ + uint32_t saved_ebx; /* 44 */ + uint32_t saved_eip; /* 48 */ + uint32_t saved_esp; /* 52 */ +}; +struct regs { + /* Be careful changing this structure + * as it is used by assembly language code. + */ + uint32_t edi; /* 0 */ + uint32_t esi; /* 4 */ + uint32_t ebp; /* 8 */ + uint32_t esp; /* 12 */ + uint32_t ebx; /* 16 */ + uint32_t edx; /* 20 */ + uint32_t ecx; /* 24 */ + uint32_t eax; /* 28 */ +}; +extern struct os_entry_regs os_regs; +extern struct regs initial_regs; +extern int xstart32(unsigned long entry_point, ...); +extern int xstart_lm(unsigned long entry_point, unsigned long params); +extern void xend32 P((void)); +struct Elf_Bhdr *prepare_boot_params(void *header); +extern int elf_start(unsigned long machine, unsigned long entry, unsigned long params); +extern unsigned long currticks P((void)); +extern void exit P((int status)); +extern void _stack; +extern char _prefix_copy[512]; +extern uint32_t image_basemem; + +/* serial.c */ +extern int serial_getc P((void)); +extern void serial_putc P((int)); +extern int serial_ischar P((void)); +extern int serial_init P((void)); +extern void serial_fini P((void)); + +/* floppy.c */ +extern int bootdisk P((int dev,int part)); + +/* pxe_callbacks.c */ +extern int pxe_in_call ( in_call_data_t *in_call_data, va_list params ); + +/*************************************************************************** +External variables +***************************************************************************/ +/* main.c */ +extern struct rom_info rom; +extern char *hostname; +extern int hostnamelen; +extern jmp_buf restart_etherboot; +extern int url_port; +extern struct arptable_t arptable[MAX_ARP]; +extern struct igmptable_t igmptable[MAX_IGMP]; +#ifdef IMAGE_MENU +extern int menutmo,menudefault; +extern unsigned char *defparams; +extern int defparams_max; +#endif +#ifdef MOTD +extern unsigned char *motd[RFC1533_VENDOR_NUMOFMOTD]; +#endif +extern struct bootpd_t bootp_data; +#define BOOTP_DATA_ADDR (&bootp_data) +extern unsigned char *end_of_rfc1533; +#ifdef IMAGE_FREEBSD +extern int freebsd_howto; +#define FREEBSD_KERNEL_ENV_SIZE 256 +extern char freebsd_kernel_env[FREEBSD_KERNEL_ENV_SIZE]; +#endif + +/* bootmenu.c */ + +/* osloader.c */ + +/* created by linker */ +extern char _virt_start[], _text[], _etext[], _text16[], _etext16[]; +extern char _data[], _edata[], _bss[], _ebss[], _end[]; + + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ + +#endif /* ETHERBOOT_H */ diff --git a/src/include/fs.h b/src/include/fs.h new file mode 100644 index 00000000..1dfe8fd9 --- /dev/null +++ b/src/include/fs.h @@ -0,0 +1,41 @@ +#ifndef FS_H +#define FS_H + +#include + +//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/src/include/http.h b/src/include/http.h new file mode 100644 index 00000000..cc1bfdfe --- /dev/null +++ b/src/include/http.h @@ -0,0 +1,7 @@ +#ifndef HTTP_H +#define HTTP_H + +extern int http(const char *url, + int (*fnc)(unsigned char *, unsigned int, unsigned int, int)); + +#endif /* HTTP_H */ diff --git a/src/include/i82365.h b/src/include/i82365.h new file mode 100644 index 00000000..3b0e00ca --- /dev/null +++ b/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 + * . 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/src/include/if_arp.h b/src/include/if_arp.h new file mode 100644 index 00000000..b3aebf74 --- /dev/null +++ b/src/include/if_arp.h @@ -0,0 +1,23 @@ +#ifndef _IF_ARP_H +#define _IF_ARP_H + +#define ARP_REQUEST 1 +#define ARP_REPLY 2 + +/* + * A pity sipaddr and tipaddr are not longword aligned or we could use + * in_addr. No, I don't want to use #pragma packed. + */ +struct arprequest { + uint16_t hwtype; + uint16_t protocol; + uint8_t hwlen; + uint8_t protolen; + uint16_t opcode; + uint8_t shwaddr[6]; + uint8_t sipaddr[4]; + uint8_t thwaddr[6]; + uint8_t tipaddr[4]; +} PACKED; + +#endif /* _IF_ARP_H */ diff --git a/src/include/if_ether.h b/src/include/if_ether.h new file mode 100644 index 00000000..c36ec889 --- /dev/null +++ b/src/include/if_ether.h @@ -0,0 +1,27 @@ +#ifndef _IF_ETHER_H +#define _IF_ETHER_H + +/* + I'm moving towards the defined names in linux/if_ether.h for clarity. + The confusion between 60/64 and 1514/1518 arose because the NS8390 + counts the 4 byte frame checksum in the incoming packet, but not + in the outgoing packet. 60/1514 are the correct numbers for most + if not all of the other NIC controllers. +*/ + +#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_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 */ + +#endif /* _IF_ETHER_H */ diff --git a/src/include/igmp.h b/src/include/igmp.h new file mode 100644 index 00000000..2235d6c6 --- /dev/null +++ b/src/include/igmp.h @@ -0,0 +1,23 @@ +#ifndef _IGMP_H +#define _IGMP_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 */ + +struct igmp { + uint8_t type; + uint8_t response_time; + uint16_t chksum; + 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; + +#endif /* _IGMP_H */ diff --git a/src/include/in.h b/src/include/in.h new file mode 100644 index 00000000..7fb207da --- /dev/null +++ b/src/include/in.h @@ -0,0 +1,21 @@ +#ifndef _IN_H +#define _IN_H + +#include +#define IP ETH_P_IP +#define ARP ETH_P_ARP +#define RARP ETH_P_RARP + +#define IP_ICMP 1 +#define IP_IGMP 2 +#define IP_TCP 6 +#define IP_UDP 17 + +/* Same after going through htonl */ +#define IP_BROADCAST 0xFFFFFFFF + +typedef struct { + uint32_t s_addr; +} in_addr; + +#endif /* _IN_H */ diff --git a/src/include/ip.h b/src/include/ip.h new file mode 100644 index 00000000..0f8fad6a --- /dev/null +++ b/src/include/ip.h @@ -0,0 +1,17 @@ +#ifndef _IP_H +#define _IP_H + +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; + in_addr src; + in_addr dest; +} PACKED; + +#endif /* _IP_H */ diff --git a/src/include/isa.h b/src/include/isa.h new file mode 100644 index 00000000..23a8f2ff --- /dev/null +++ b/src/include/isa.h @@ -0,0 +1,32 @@ +#ifndef ISA_H +#define ISA_H + +struct dev; + +#define ISAPNP_VENDOR(a,b,c) (((((a)-'A'+1)&0x3f)<<2)|\ + ((((b)-'A'+1)&0x18)>>3)|((((b)-'A'+1)&7)<<13)|\ + ((((c)-'A'+1)&0x1f)<<8)) + +#define GENERIC_ISAPNP_VENDOR ISAPNP_VENDOR('P','N','P') + +struct isa_driver +{ + int type; + const char *name; + int (*probe)(struct dev *, unsigned short *); + unsigned short *ioaddrs; +}; + +#ifndef __HYPERSTONE__ +#define __isa_driver __attribute__ ((used,__section__(".drivers.isa"))) +#else +#define __isa_driver __attribute__ ((used,__section__(".drivisa"))) +#endif + +extern const struct isa_driver isa_drivers[]; +extern const struct isa_driver isa_drivers_end[]; + +#define ISA_ROM(IMAGE, DESCRIPTION) + +#endif /* ISA_H */ + diff --git a/src/include/isapnp.h b/src/include/isapnp.h new file mode 100644 index 00000000..228823b2 --- /dev/null +++ b/src/include/isapnp.h @@ -0,0 +1,124 @@ +/************************************************************************** +* +* isapnp.h -- Etherboot isapnp support for the 3Com 3c515 +* Written 2002-2003 by Timothy Legge +* +* 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 +* +***************************************************************************/ + +/*extern int read_port;*/ +/*#define DEBUG*/ +#define ADDRESS_ADDR 0x0279 +#define WRITEDATA_ADDR 0x0a79 +/* MIN and MAX READ_ADDR must have the bottom two bits set */ +#define MIN_READ_ADDR 0x0203 +#define START_READ_ADDR 0x203 +#define MAX_READ_ADDR 0x03ff +/* READ_ADDR_STEP must be a multiple of 4 */ +#ifndef READ_ADDR_STEP +#define READ_ADDR_STEP 8 +#endif + +#ifdef EDEBUG +static int x; +#define ADDRESS(x) (outb(x, ADDRESS_ADDR), printf("\nAddress: %hX", x)) +#define WRITE_DATA(x) (outb(x, WRITEDATA_ADDR), printf(" WR(%hX)", x & 0xff)) +#define READ_DATA (x = inb(read_port), printf(" RD(%hX)", x & 0xff), x) +#define READ_IOPORT(p) (x = inb(p), printf(" [%hX](%hX)", p, x & 0xff), x) +#else /* !DEBUG */ +#define ADDRESS(x) outb(x, ADDRESS_ADDR) +#define WRITE_DATA(x) outb(x, WRITEDATA_ADDR) +#define READ_DATA inb(read_port) +#define READ_IOPORT(p) inb(p) +#endif /* !DEBUG */ + + + +#define INIT_LENGTH 32 + +#define INITDATA { 0x6a, 0xb5, 0xda, 0xed, 0xf6, 0xfb, 0x7d, 0xbe,\ + 0xdf, 0x6f, 0x37, 0x1b, 0x0d, 0x86, 0xc3, 0x61,\ + 0xb0, 0x58, 0x2c, 0x16, 0x8b, 0x45, 0xa2, 0xd1,\ + 0xe8, 0x74, 0x3a, 0x9d, 0xce, 0xe7, 0x73, 0x39 } + +/* Registers */ +#define SetRdPort(x) (ADDRESS(0x00),WRITE_DATA((x)>>2),read_port=((x)|3)) +#define SERIALISOLATION ADDRESS(0x01) +#define CONFIGCONTROL ADDRESS(0x02) +#define Wake(x) (ADDRESS(0x03),WRITE_DATA(x)) +#define RESOURCEDATA (ADDRESS(0x04),READ_DATA) +#define STATUS (ADDRESS(0x05),READ_DATA) +#define CARDSELECTNUMBER ADDRESS(0x06) +#define LOGICALDEVICENUMBER ADDRESS(0x07) +#define ACTIVATE ADDRESS(0x30) +#define IORANGECHECK ADDRESS(0x31) + +/* Bits */ +#define CONFIG_RESET 0x01 +#define CONFIG_WAIT_FOR_KEY 0x02 +#define CONFIG_RESET_CSN 0x04 +#define CONFIG_RESET_DRV 0x07 + +/* Short Tags */ +#define PnPVerNo_TAG 0x01 +#define LogDevId_TAG 0x02 +#define CompatDevId_TAG 0x03 +#define IRQ_TAG 0x04 +#define DMA_TAG 0x05 +#define StartDep_TAG 0x06 +#define EndDep_TAG 0x07 +#define IOport_TAG 0x08 +#define FixedIO_TAG 0x09 +#define RsvdShortA_TAG 0x0A +#define RsvdShortB_TAG 0x0B +#define RsvdShortC_TAG 0x0C +#define RsvdShortD_TAG 0x0D +#define VendorShort_TAG 0x0E +#define End_TAG 0x0F +/* Long Tags */ +#define MemRange_TAG 0x81 +#define ANSIstr_TAG 0x82 +#define UNICODEstr_TAG 0x83 +#define VendorLong_TAG 0x84 +#define Mem32Range_TAG 0x85 +#define FixedMem32Range_TAG 0x86 +#define RsvdLong0_TAG 0xF0 +#define RsvdLong1_TAG 0xF1 +#define RsvdLong2_TAG 0xF2 +#define RsvdLong3_TAG 0xF3 +#define RsvdLong4_TAG 0xF4 +#define RsvdLong5_TAG 0xF5 +#define RsvdLong6_TAG 0xF6 +#define RsvdLong7_TAG 0xF7 +#define RsvdLong8_TAG 0xF8 +#define RsvdLong9_TAG 0xF9 +#define RsvdLongA_TAG 0xFA +#define RsvdLongB_TAG 0xFB +#define RsvdLongC_TAG 0xFC +#define RsvdLongD_TAG 0xFD +#define RsvdLongE_TAG 0xFE +#define RsvdLongF_TAG 0xFF +#define NewBoard_PSEUDOTAG 0x100 diff --git a/src/include/lib.h b/src/include/lib.h new file mode 100644 index 00000000..400ea468 --- /dev/null +++ b/src/include/lib.h @@ -0,0 +1,42 @@ +#ifndef LIB_H +#define LIB_H + +#include + +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/src/include/little_bswap.h b/src/include/little_bswap.h new file mode 100644 index 00000000..35918530 --- /dev/null +++ b/src/include/little_bswap.h @@ -0,0 +1,17 @@ +#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_le32(x) (x) +#define cpu_to_le16(x) (x) +#define cpu_to_be32(x) __bswap_32(x) +#define cpu_to_be16(x) __bswap_16(x) +#define le32_to_cpu(x) (x) +#define le16_to_cpu(x) (x) +#define be32_to_cpu(x) __bswap_32(x) +#define be16_to_cpu(x) __bswap_16(x) + +#endif /* ETHERBOOT_LITTLE_BSWAP_H */ diff --git a/src/include/mii.h b/src/include/mii.h new file mode 100644 index 00000000..34c1ca9b --- /dev/null +++ b/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/src/include/nfs.h b/src/include/nfs.h new file mode 100644 index 00000000..0877bb66 --- /dev/null +++ b/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/src/include/nic.h b/src/include/nic.h new file mode 100644 index 00000000..c258d745 --- /dev/null +++ b/src/include/nic.h @@ -0,0 +1,49 @@ + /* + * 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 "dev.h" + +typedef enum { + DISABLE = 0, + ENABLE, + FORCE +} irq_action_t; + +/* + * Structure returned from eth_probe and passed to other driver + * functions. + */ +struct nic +{ + struct dev dev; /* This must come first */ + int (*poll)P((struct nic *, int retrieve)); + void (*transmit)P((struct nic *, const char *d, + unsigned int t, unsigned int s, const char *p)); + void (*irq)P((struct nic *, irq_action_t)); + int flags; /* driver specific flags */ + struct rom_info *rom_info; /* -> rom_info from main */ + unsigned char *node_addr; + unsigned char *packet; + unsigned int packetlen; + unsigned int ioaddr; + unsigned char irqno; + void *priv_data; /* driver can hang private data here */ +}; + + +extern struct nic nic; +extern int eth_probe(struct dev *dev); +extern int eth_poll(int retrieve); +extern void eth_transmit(const char *d, unsigned int t, unsigned int s, const void *p); +extern void eth_disable(void); +extern void eth_irq(irq_action_t action); +extern int eth_load_configuration(struct dev *dev); +extern int eth_load(struct dev *dev);; +#endif /* NIC_H */ diff --git a/src/include/osdep.h b/src/include/osdep.h new file mode 100644 index 00000000..1b03e567 --- /dev/null +++ b/src/include/osdep.h @@ -0,0 +1,43 @@ +#ifndef ETHERBOOT_OSDEP_H +#define ETHERBOOT_OSDEP_H + +#define __unused __attribute__((unused)) +#define __aligned __attribute__((aligned(16))) +#define PACKED __attribute__((packed)) + +/* Optimization barrier */ +/* The "volatile" is due to gcc bugs */ +#define barrier() __asm__ __volatile__("": : :"memory") + +#include "stdint.h" +#include "limits.h" +#include "string.h" +#include "io.h" +#include "endian.h" +#include "byteswap.h" +#include "setjmp.h" +#include "latch.h" +#include "callbacks.h" +#include "hooks.h" + +/* within 1MB of 4GB is too close. + * MAX_ADDR is the maximum address we can easily do DMA to. + */ +#define MAX_ADDR (0xfff00000UL) + +typedef unsigned long Address; + +/* ANSI prototyping macro */ +#ifdef __STDC__ +#define P(x) x +#else +#define P(x) () +#endif + +#endif + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/src/include/pc_kbd.h b/src/include/pc_kbd.h new file mode 100644 index 00000000..c125efa0 --- /dev/null +++ b/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/src/include/pci.h b/src/include/pci.h new file mode 100644 index 00000000..236e0bc9 --- /dev/null +++ b/src/include/pci.h @@ -0,0 +1,362 @@ +#ifndef PCI_H +#define PCI_H + +/* +** Support for NE2000 PCI clones added David Monro June 1997 +** Generalised for other PCI NICs by Ken Yap July 1997 +** +** Most of this is taken from: +** +** /usr/src/linux/drivers/pci/pci.c +** /usr/src/linux/include/linux/pci.h +** /usr/src/linux/arch/i386/bios32.c +** /usr/src/linux/include/linux/bios32.h +** /usr/src/linux/drivers/net/ne.c +*/ + +/* + * 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 "pci_ids.h" + +#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 PCIBIOS_PCI_FUNCTION_ID 0xb1XX +#define PCIBIOS_PCI_BIOS_PRESENT 0xb101 +#define PCIBIOS_FIND_PCI_DEVICE 0xb102 +#define PCIBIOS_FIND_PCI_CLASS_CODE 0xb103 +#define PCIBIOS_GENERATE_SPECIAL_CYCLE 0xb106 +#define PCIBIOS_READ_CONFIG_BYTE 0xb108 +#define PCIBIOS_READ_CONFIG_WORD 0xb109 +#define PCIBIOS_READ_CONFIG_DWORD 0xb10a +#define PCIBIOS_WRITE_CONFIG_BYTE 0xb10b +#define PCIBIOS_WRITE_CONFIG_WORD 0xb10c +#define PCIBIOS_WRITE_CONFIG_DWORD 0xb10d + +#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_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 */ + +#ifndef PCI_BASE_ADDRESS_IO_MASK +#define PCI_BASE_ADDRESS_IO_MASK (~0x03) +#endif +#ifndef PCI_BASE_ADDRESS_MEM_MASK +#define PCI_BASE_ADDRESS_MEM_MASK (~0x0f) +#endif +#define PCI_BASE_ADDRESS_SPACE_IO 0x01 +#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 */ + +#define PCI_SLOT(devfn) ((devfn) >> 3) +#define PCI_FUNC(devfn) ((devfn) & 0x07) + +#define BIOS32_SIGNATURE (('_' << 0) + ('3' << 8) + ('2' << 16) + ('_' << 24)) + +/* PCI signature: "PCI " */ +#define PCI_SIGNATURE (('P' << 0) + ('C' << 8) + ('I' << 16) + (' ' << 24)) + +/* PCI service signature: "$PCI" */ +#define PCI_SERVICE (('$' << 0) + ('P' << 8) + ('C' << 16) + ('I' << 24)) + +union bios32 { + struct { + unsigned long signature; /* _32_ */ + unsigned long entry; /* 32 bit physical address */ + unsigned char revision; /* Revision level, 0 */ + unsigned char length; /* Length in paragraphs should be 01 */ + unsigned char checksum; /* All bytes must add up to zero */ + unsigned char reserved[5]; /* Must be zero */ + } fields; + char chars[16]; +}; + +struct pci_device; +struct dev; +typedef int (*pci_probe_t)(struct dev *, struct pci_device *); + +struct pci_device { + uint32_t class; + uint16_t vendor, dev_id; + const char *name; + /* membase and ioaddr are silly and depricated */ + unsigned int membase; + unsigned int ioaddr; + unsigned int romaddr; + unsigned char irq; + unsigned char devfn; + unsigned char bus; + unsigned char use_specified; + const struct pci_driver *driver; +}; + +extern void scan_pci_bus(int type, struct pci_device *dev); +extern void find_pci(int type, struct pci_device *dev); + +extern int pcibios_read_config_byte(unsigned int bus, unsigned int device_fn, unsigned int where, uint8_t *value); +extern int pcibios_write_config_byte (unsigned int bus, unsigned int device_fn, unsigned int where, uint8_t value); +extern int pcibios_read_config_word(unsigned int bus, unsigned int device_fn, unsigned int where, uint16_t *value); +extern int pcibios_write_config_word (unsigned int bus, unsigned int device_fn, unsigned int where, uint16_t value); +extern int pcibios_read_config_dword(unsigned int bus, unsigned int device_fn, unsigned int where, uint32_t *value); +extern int pcibios_write_config_dword(unsigned int bus, unsigned int device_fn, unsigned int where, uint32_t value); +extern unsigned long pcibios_bus_base(unsigned int bus); +extern void adjust_pci_device(struct pci_device *p); + + +static inline int +pci_read_config_byte(struct pci_device *dev, unsigned int where, uint8_t *value) +{ + return pcibios_read_config_byte(dev->bus, dev->devfn, where, value); +} +static inline int +pci_write_config_byte(struct pci_device *dev, unsigned int where, uint8_t value) +{ + return pcibios_write_config_byte(dev->bus, dev->devfn, where, value); +} +static inline int +pci_read_config_word(struct pci_device *dev, unsigned int where, uint16_t *value) +{ + return pcibios_read_config_word(dev->bus, dev->devfn, where, value); +} +static inline int +pci_write_config_word(struct pci_device *dev, unsigned int where, uint16_t value) +{ + return pcibios_write_config_word(dev->bus, dev->devfn, where, value); +} +static inline int +pci_read_config_dword(struct pci_device *dev, unsigned int where, uint32_t *value) +{ + return pcibios_read_config_dword(dev->bus, dev->devfn, where, value); +} +static inline int +pci_write_config_dword(struct pci_device *dev, unsigned int where, uint32_t value) +{ + return pcibios_write_config_dword(dev->bus, dev->devfn, where, value); +} + +/* Helper functions to find the size of a pci bar */ +extern unsigned long pci_bar_start(struct pci_device *dev, unsigned int bar); +extern unsigned long pci_bar_size(struct pci_device *dev, unsigned int bar); +/* Helper function to find pci capabilities */ +extern int pci_find_capability(struct pci_device *dev, int cap); +struct pci_id { + unsigned short vendor, dev_id; + const char *name; +}; + +struct dev; +/* Most pci drivers will use this */ +struct pci_driver { + int type; + const char *name; + pci_probe_t probe; + struct pci_id *ids; + int id_count; + +/* On a few occasions the hardware is standardized enough that + * we only need to know the class of the device and not the exact + * type to drive the device correctly. If this is the case + * set a class value other than 0. + */ + unsigned short class; +}; + +#define __pci_driver __attribute__ ((used,__section__(".drivers.pci"))) +/* Defined by the linker... */ +extern const struct pci_driver pci_drivers[]; +extern const struct pci_driver pci_drivers_end[]; + +#define PCI_ROM(VENDOR_ID, DEVICE_ID, IMAGE, DESCRIPTION) \ + { VENDOR_ID, DEVICE_ID, IMAGE, } + +#endif /* PCI_H */ diff --git a/src/include/pci_ids.h b/src/include/pci_ids.h new file mode 100644 index 00000000..bcf4c3cd --- /dev/null +++ b/src/include/pci_ids.h @@ -0,0 +1,342 @@ +/* + * 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_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 diff --git a/src/include/pcmcia-opts.h b/src/include/pcmcia-opts.h new file mode 100644 index 00000000..70dc0921 --- /dev/null +++ b/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/src/include/pcmcia.h b/src/include/pcmcia.h new file mode 100644 index 00000000..6b8bc327 --- /dev/null +++ b/src/include/pcmcia.h @@ -0,0 +1,157 @@ +// pcmcia.h - Header file for PCMCIA support + +#ifndef INCLUDE_PCMCIA_H +#define INCLUDE_PCMCIA_H 1 +#include "etherboot.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 diff --git a/src/include/pxe.h b/src/include/pxe.h new file mode 100644 index 00000000..48f55509 --- /dev/null +++ b/src/include/pxe.h @@ -0,0 +1,929 @@ +/* + * pxe.h for Etherboot. + * + * PXE is technically specified only for i386, but there's no reason + * why we shouldn't make the API available for other architectures, + * provided that someone wants to write the shim that allows an + * external program to call pxe_api_call(). + * + * We stick with Intel's data structure definitions as far as possible + * on other architectures. Generally the only i386-specific stuff is + * related to addressing: real-mode segment:offset addresses, segment + * selectors, segment descriptors etc. We allow an architecture- + * specific header to define these types, then build the PXE + * structures. Note that we retain the names from the PXE + * specification document (e.g. SEGOFF16_t) even if the architecture + * in question doesn't represent a SEGOFF16_t as anything resembling a + * 16-bit segment:offset address. This is done in order to keep the + * structure definitions as close as possible to those in the spec, to + * minimise confusion. + * + * This file derives from several originals. One is pxe.h from + * FreeBSD. Another is general.h86 from netboot. The original + * copyright notices are reproduced below. This entire file is + * licensed under the GPL; the netboot code is GPL anyway and the + * FreeBSD code allows us to relicense under the GPL provided that we + * retain the FreeBSD copyright notice. This is my understanding, + * anyway. Other portions are my own and therefore Copyright (C) 2004 + * Michael Brown . + * + * 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. + */ + +#ifndef PXE_H +#define PXE_H + +/* Include architecture-specific PXE data types + * + * May define SEGOFF16_t, SEGDESC_t and SEGSEL_t. These should be + * #defines to underlying * types. May also define + * IS_NULL_SEGOFF16(segoff16), SEGOFF16_TO_PTR(segoff16) and + * PTR_TO_SEGOFF16(ptr,segoff16) + */ +#ifndef PXE_TYPES_H +#include +#endif + +/* Defaults in case pxe_types.h did not define a type. These are + * placeholder structures just to make the code compile. + */ +#ifndef SEGOFF16_t +#define SEGOFF16_t void* +#endif + +#ifndef IS_NULL_SEGOFF16 +#define IS_NULL_SEGOFF16(segoff16) ( (segoff16) == NULL ) +#endif + +#ifndef SEGOFF16_TO_PTR +#define SEGOFF16_TO_PTR(segoff16) (segoff16) +#endif + +#ifndef PTR_TO_SEGOFF16 +#define PTR_TO_SEGOFF16(ptr,segoff16) (segoff16) = (ptr); +#endif + +#ifndef SEGDESC_t +#define SEGDESC_t void +#endif + +#ifndef SEGSEL_t +#define SEGSEL_t void +#endif + +/***************************************************************************** + * The following portion of this file is derived from FreeBSD's pxe.h. + * Do not remove the copyright notice below. + ***************************************************************************** + */ + +/* + * Copyright (c) 2000 Alfred Perlstein + * All rights reserved. + * Copyright (c) 2000 Paul Saab + * All rights reserved. + * Copyright (c) 2000 John Baldwin + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD: src/sys/boot/i386/libi386/pxe.h,v 1.4.2.2 2000/09/10 02:52:18 ps Exp $ + */ + +/* + * The typedefs and structures declared in this file + * clearly violate style(9), the reason for this is to conform to the + * typedefs/structure-names used in the Intel literature to avoid confusion. + * + * It's for your own good. :) + */ + +/* It seems that intel didn't think about ABI, + * either that or 16bit ABI != 32bit ABI (which seems reasonable) + * I have to thank Intel for the hair loss I incurred trying to figure + * out why PXE was mis-reading structures I was passing it (at least + * from my point of view) + * + * Solution: use gcc's '__attribute__ ((packed))' to correctly align + * structures passed into PXE + * Question: does this really work for PXE's expected ABI? + */ +#ifndef PACKED +#define PACKED __attribute__ ((packed)) +#endif + +#define S_SIZE(s) s, sizeof(s) - 1 + +#define IP_STR "%d.%d.%d.%d" +#define IP_ARGS(ip) \ + (int)(ip >> 24) & 0xff, (int)(ip >> 16) & 0xff, \ + (int)(ip >> 8) & 0xff, (int)ip & 0xff + +#define MAC_STR "%02x:%02x:%02x:%02x:%02x:%02x" +#define MAC_ARGS(mac) \ + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] + +typedef uint16_t PXENV_EXIT_t; +typedef uint16_t PXENV_STATUS_t; +typedef uint32_t IP4_t; +typedef uint32_t ADDR32_t; +/* It seems as though UDP_PORT_t is in network order, although I can't + * find anything in the spec to back this up. (Michael Brown) + */ +typedef uint16_t UDP_PORT_t; + +#define MAC_ADDR_LEN 16 +typedef uint8_t MAC_ADDR[MAC_ADDR_LEN]; + +/* PXENV+ */ +typedef struct { + uint8_t Signature[6]; /* 'PXENV+' */ + uint16_t Version; /* MSB = major, LSB = minor */ + uint8_t Length; /* structure length */ + uint8_t Checksum; /* checksum pad */ + SEGOFF16_t RMEntry; /* SEG:OFF to PXE entry point */ + /* don't use PMOffset and PMSelector (from the 2.1 PXE manual) */ + uint32_t PMOffset; /* Protected mode entry */ + SEGSEL_t PMSelector; /* Protected mode selector */ + SEGSEL_t StackSeg; /* Stack segment address */ + uint16_t StackSize; /* Stack segment size (bytes) */ + SEGSEL_t BC_CodeSeg; /* BC Code segment address */ + uint16_t BC_CodeSize; /* BC Code segment size (bytes) */ + SEGSEL_t BC_DataSeg; /* BC Data segment address */ + uint16_t BC_DataSize; /* BC Data segment size (bytes) */ + SEGSEL_t UNDIDataSeg; /* UNDI Data segment address */ + uint16_t UNDIDataSize; /* UNDI Data segment size (bytes) */ + SEGSEL_t UNDICodeSeg; /* UNDI Code segment address */ + uint16_t UNDICodeSize; /* UNDI Code segment size (bytes) */ + SEGOFF16_t PXEPtr; /* SEG:OFF to !PXE struct, + only present when Version > 2.1 */ +} PACKED pxenv_t; + +/* !PXE */ +typedef struct { + uint8_t Signature[4]; + uint8_t StructLength; + uint8_t StructCksum; + uint8_t StructRev; + uint8_t reserved_1; + SEGOFF16_t UNDIROMID; + SEGOFF16_t BaseROMID; + SEGOFF16_t EntryPointSP; + SEGOFF16_t EntryPointESP; + SEGOFF16_t StatusCallout; + uint8_t reserved_2; + uint8_t SegDescCn; + SEGSEL_t FirstSelector; + SEGDESC_t Stack; + SEGDESC_t UNDIData; + SEGDESC_t UNDICode; + SEGDESC_t UNDICodeWrite; + SEGDESC_t BC_Data; + SEGDESC_t BC_Code; + SEGDESC_t BC_CodeWrite; +} PACKED pxe_t; + +#define PXENV_START_UNDI 0x0000 +typedef struct { + PXENV_STATUS_t Status; + uint16_t ax; + uint16_t bx; + uint16_t dx; + uint16_t di; + uint16_t es; +} PACKED t_PXENV_START_UNDI; + +#define PXENV_UNDI_STARTUP 0x0001 +typedef struct { + PXENV_STATUS_t Status; +} PACKED t_PXENV_UNDI_STARTUP; + +#define PXENV_UNDI_CLEANUP 0x0002 +typedef struct { + PXENV_STATUS_t Status; +} PACKED t_PXENV_UNDI_CLEANUP; + +#define PXENV_UNDI_INITIALIZE 0x0003 +typedef struct { + PXENV_STATUS_t Status; + ADDR32_t ProtocolIni; /* Phys addr of a copy of the driver module */ + uint8_t reserved[8]; +} PACKED t_PXENV_UNDI_INITIALIZE; + + +#define MAXNUM_MCADDR 8 +typedef struct { + uint16_t MCastAddrCount; + MAC_ADDR McastAddr[MAXNUM_MCADDR]; +} PACKED t_PXENV_UNDI_MCAST_ADDRESS; + +#define PXENV_UNDI_RESET_ADAPTER 0x0004 +typedef struct { + PXENV_STATUS_t Status; + t_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf; +} PACKED t_PXENV_UNDI_RESET_ADAPTER; + +#define PXENV_UNDI_SHUTDOWN 0x0005 +typedef struct { + PXENV_STATUS_t Status; +} PACKED t_PXENV_UNDI_SHUTDOWN; + +#define PXENV_UNDI_OPEN 0x0006 +typedef struct { + PXENV_STATUS_t Status; + uint16_t OpenFlag; + uint16_t PktFilter; +# define FLTR_DIRECTED 0x0001 +# define FLTR_BRDCST 0x0002 +# define FLTR_PRMSCS 0x0003 +# define FLTR_SRC_RTG 0x0004 + + t_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf; +} PACKED t_PXENV_UNDI_OPEN; + +#define PXENV_UNDI_CLOSE 0x0007 +typedef struct { + PXENV_STATUS_t Status; +} PACKED t_PXENV_UNDI_CLOSE; + +#define PXENV_UNDI_TRANSMIT 0x0008 +typedef struct { + PXENV_STATUS_t Status; + uint8_t Protocol; +# define P_UNKNOWN 0 +# define P_IP 1 +# define P_ARP 2 +# define P_RARP 3 + + uint8_t XmitFlag; +# define XMT_DESTADDR 0x0000 +# define XMT_BROADCAST 0x0001 + + SEGOFF16_t DestAddr; + SEGOFF16_t TBD; + uint32_t Reserved[2]; +} PACKED t_PXENV_UNDI_TRANSMIT; + +#define MAX_DATA_BLKS 8 +typedef struct { + uint16_t ImmedLength; + SEGOFF16_t Xmit; + uint16_t DataBlkCount; + struct DataBlk { + uint8_t TDPtrType; + uint8_t TDRsvdByte; + uint16_t TDDataLen; + SEGOFF16_t TDDataPtr; + } DataBlock[MAX_DATA_BLKS]; +} PACKED t_PXENV_UNDI_TBD; + +#define PXENV_UNDI_SET_MCAST_ADDRESS 0x0009 +typedef struct { + PXENV_STATUS_t Status; + t_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf; +} PACKED t_PXENV_UNDI_SET_MCAST_ADDRESS; + +#define PXENV_UNDI_SET_STATION_ADDRESS 0x000A +typedef struct { + PXENV_STATUS_t Status; + MAC_ADDR StationAddress; /* Temp MAC addres to use */ +} PACKED t_PXENV_UNDI_SET_STATION_ADDRESS; + +#define PXENV_UNDI_SET_PACKET_FILTER 0x000B +typedef struct { + PXENV_STATUS_t Status; + uint8_t filter; /* see UNDI_OPEN (0x0006) */ +} PACKED t_PXENV_UNDI_SET_PACKET_FILTER; + +#define PXENV_UNDI_GET_INFORMATION 0x000C +typedef struct { + PXENV_STATUS_t Status; + uint16_t BaseIo; /* Adapter base I/O address */ + uint16_t IntNumber; /* Adapter IRQ number */ + uint16_t MaxTranUnit; /* Adapter maximum transmit unit */ + uint16_t HwType; /* Type of protocol at the hardware addr */ +# define ETHER_TYPE 1 +# define EXP_ETHER_TYPE 2 +# define IEEE_TYPE 6 +# define ARCNET_TYPE 7 + + uint16_t HwAddrLen; /* Length of hardware address */ + MAC_ADDR CurrentNodeAddress; /* Current hardware address */ + MAC_ADDR PermNodeAddress; /* Permanent hardware address */ + SEGSEL_t ROMAddress; /* Real mode ROM segment address */ + uint16_t RxBufCt; /* Receive queue length */ + uint16_t TxBufCt; /* Transmit queue length */ +} PACKED t_PXENV_UNDI_GET_INFORMATION; + +#define PXENV_UNDI_GET_STATISTICS 0x000D +typedef struct { + PXENV_STATUS_t Status; + uint32_t XmitGoodFrames; /* Number of successful transmissions */ + uint32_t RcvGoodFrames; /* Number of good frames received */ + uint32_t RcvCRCErrors; /* Number of frames with CRC errors */ + uint32_t RcvResourceErrors; /* Number of frames dropped */ +} PACKED t_PXENV_UNDI_GET_STATISTICS; + +#define PXENV_UNDI_CLEAR_STATISTICS 0x000E +typedef struct { + PXENV_STATUS_t Status; +} PACKED t_PXENV_UNDI_CLEAR_STATISTICS; + +#define PXENV_UNDI_INITIATE_DIAGS 0x000F +typedef struct { + PXENV_STATUS_t Status; +} PACKED t_PXENV_UNDI_INITIATE_DIAGS; + +#define PXENV_UNDI_FORCE_INTERRUPT 0x0010 +typedef struct { + PXENV_STATUS_t Status; +} PACKED t_PXENV_UNDI_FORCE_INTERRUPT; + +#define PXENV_UNDI_GET_MCAST_ADDRESS 0x0011 +typedef struct { + PXENV_STATUS_t Status; + IP4_t InetAddr; /* IP mulicast address */ + MAC_ADDR MediaAddr; /* MAC multicast address */ +} PACKED t_PXENV_UNDI_GET_MCAST_ADDRESS; + +#define PXENV_UNDI_GET_NIC_TYPE 0x0012 +typedef struct { + PXENV_STATUS_t Status; + uint8_t NicType; /* Type of NIC */ +# define PCI_NIC 2 +# define PnP_NIC 3 +# define CardBus_NIC 4 + + union { + struct { + uint16_t Vendor_ID; + uint16_t Dev_ID; + uint8_t Base_Class; + uint8_t Sub_Class; + uint8_t Prog_Intf; + uint8_t Rev; + uint16_t BusDevFunc; + uint16_t SubVendor_ID; + uint16_t SubDevice_ID; + } pci, cardbus; + struct { + uint32_t EISA_Dev_ID; + uint8_t Base_Class; + uint8_t Sub_Class; + uint8_t Prog_Intf; + uint16_t CardSelNum; + } pnp; + } info; +} PACKED t_PXENV_UNDI_GET_NIC_TYPE; + +#define PXENV_UNDI_GET_IFACE_INFO 0x0013 +typedef struct { + PXENV_STATUS_t Status; + uint8_t IfaceType[16]; /* Name of MAC type in ASCII. */ + uint32_t LinkSpeed; /* Defined in NDIS 2.0 spec */ + uint32_t ServiceFlags; /* Defined in NDIS 2.0 spec */ + uint32_t Reserved[4]; /* must be 0 */ +} PACKED t_PXENV_UNDI_GET_IFACE_INFO; + +#define PXENV_UNDI_ISR 0x0014 +typedef struct { + PXENV_STATUS_t Status; + uint16_t FuncFlag; /* PXENV_UNDI_ISR_OUT_xxx */ + uint16_t BufferLength; /* Length of Frame */ + uint16_t FrameLength; /* Total length of reciever frame */ + uint16_t FrameHeaderLength; /* Length of the media header in Frame */ + SEGOFF16_t Frame; /* receive buffer */ + uint8_t ProtType; /* Protocol type */ + uint8_t PktType; /* Packet Type */ +# define PXENV_UNDI_ISR_IN_START 1 +# define PXENV_UNDI_ISR_IN_PROCESS 2 +# define PXENV_UNDI_ISR_IN_GET_NEXT 3 + + /* one of these will be returned for PXENV_UNDI_ISR_IN_START */ +# define PXENV_UNDI_ISR_OUT_OURS 0 +# define PXENV_UNDI_ISR_OUT_NOT_OURS 1 + + /* + * one of these will bre returnd for PXEND_UNDI_ISR_IN_PROCESS + * and PXENV_UNDI_ISR_IN_GET_NEXT + */ +# define PXENV_UNDI_ISR_OUT_DONE 0 +# define PXENV_UNDI_ISR_OUT_TRANSMIT 2 +# define PXENV_UNDI_ISR_OUT_RECEIVE 3 +# define PXENV_UNDI_ISR_OUT_BUSY 4 +} PACKED t_PXENV_UNDI_ISR; + +#define PXENV_STOP_UNDI 0x0015 +typedef struct { + PXENV_STATUS_t Status; +} PACKED t_PXENV_STOP_UNDI; + +#define PXENV_TFTP_OPEN 0x0020 +typedef struct { + PXENV_STATUS_t Status; + IP4_t ServerIPAddress; + IP4_t GatewayIPAddress; + uint8_t FileName[128]; + UDP_PORT_t TFTPPort; + uint16_t PacketSize; +} PACKED t_PXENV_TFTP_OPEN; + +#define PXENV_TFTP_CLOSE 0x0021 +typedef struct { + PXENV_STATUS_t Status; +} PACKED t_PXENV_TFTP_CLOSE; + +#define PXENV_TFTP_READ 0x0022 +typedef struct { + PXENV_STATUS_t Status; + uint16_t PacketNumber; + uint16_t BufferSize; + SEGOFF16_t Buffer; +} PACKED t_PXENV_TFTP_READ; + +#define PXENV_TFTP_READ_FILE 0x0023 +typedef struct { + PXENV_STATUS_t Status; + uint8_t FileName[128]; + uint32_t BufferSize; + ADDR32_t Buffer; + IP4_t ServerIPAddress; + IP4_t GatewayIPAdress; + IP4_t McastIPAdress; + UDP_PORT_t TFTPClntPort; + UDP_PORT_t TFTPSrvPort; + uint16_t TFTPOpenTimeOut; + uint16_t TFTPReopenDelay; +} PACKED t_PXENV_TFTP_READ_FILE; + +#define PXENV_TFTP_GET_FSIZE 0x0025 +typedef struct { + PXENV_STATUS_t Status; + IP4_t ServerIPAddress; + IP4_t GatewayIPAdress; + uint8_t FileName[128]; + uint32_t FileSize; +} PACKED t_PXENV_TFTP_GET_FSIZE; + +#define PXENV_UDP_OPEN 0x0030 +typedef struct { + PXENV_STATUS_t Status; + IP4_t src_ip; /* IP address of this station */ +} PACKED t_PXENV_UDP_OPEN; + +#define PXENV_UDP_CLOSE 0x0031 +typedef struct { + PXENV_STATUS_t Status; +} PACKED t_PXENV_UDP_CLOSE; + +#define PXENV_UDP_READ 0x0032 +typedef struct { + PXENV_STATUS_t Status; + IP4_t src_ip; /* IP of sender */ + IP4_t dest_ip; /* Only accept packets sent to this IP */ + UDP_PORT_t s_port; /* UDP source port of sender */ + UDP_PORT_t d_port; /* Only accept packets sent to this port */ + uint16_t buffer_size; /* Size of the packet buffer */ + SEGOFF16_t buffer; /* SEG:OFF to the packet buffer */ +} PACKED t_PXENV_UDP_READ; + +#define PXENV_UDP_WRITE 0x0033 +typedef struct { + PXENV_STATUS_t Status; + IP4_t ip; /* dest ip addr */ + IP4_t gw; /* ip gateway */ + UDP_PORT_t src_port; /* source udp port */ + UDP_PORT_t dst_port; /* destination udp port */ + uint16_t buffer_size; /* Size of the packet buffer */ + SEGOFF16_t buffer; /* SEG:OFF to the packet buffer */ +} PACKED t_PXENV_UDP_WRITE; + +#define PXENV_UNLOAD_STACK 0x0070 +typedef struct { + PXENV_STATUS_t Status; + uint8_t reserved[10]; +} PACKED t_PXENV_UNLOAD_STACK; + + +#define PXENV_GET_CACHED_INFO 0x0071 +typedef struct { + PXENV_STATUS_t Status; + uint16_t PacketType; /* type (defined right here) */ +# define PXENV_PACKET_TYPE_DHCP_DISCOVER 1 +# define PXENV_PACKET_TYPE_DHCP_ACK 2 +# define PXENV_PACKET_TYPE_BINL_REPLY 3 + uint16_t BufferSize; /* max to copy, leave at 0 for pointer */ + SEGOFF16_t Buffer; /* copy to, leave at 0 for pointer */ + uint16_t BufferLimit; /* max size of buffer in BC dataseg ? */ +} PACKED t_PXENV_GET_CACHED_INFO; + + +/* structure filled in by PXENV_GET_CACHED_INFO + * (how we determine which IP we downloaded the initial bootstrap from) + * words can't describe... + */ +typedef struct { + uint8_t opcode; +# define BOOTP_REQ 1 +# define BOOTP_REP 2 + uint8_t Hardware; /* hardware type */ + uint8_t Hardlen; /* hardware addr len */ + uint8_t Gatehops; /* zero it */ + uint32_t ident; /* random number chosen by client */ + uint16_t seconds; /* seconds since did initial bootstrap */ + uint16_t Flags; /* seconds since did initial bootstrap */ +# define BOOTP_BCAST 0x8000 /* ? */ + IP4_t cip; /* Client IP */ + IP4_t yip; /* Your IP */ + IP4_t sip; /* IP to use for next boot stage */ + IP4_t gip; /* Relay IP ? */ + MAC_ADDR CAddr; /* Client hardware address */ + uint8_t Sname[64]; /* Server's hostname (Optional) */ + uint8_t bootfile[128]; /* boot filename */ + union { +# if 1 +# define BOOTP_DHCPVEND 1024 /* DHCP extended vendor field size */ +# else +# define BOOTP_DHCPVEND 312 /* DHCP standard vendor field size */ +# endif + uint8_t d[BOOTP_DHCPVEND]; /* raw array of vendor/dhcp options */ + struct { + uint8_t magic[4]; /* DHCP magic cookie */ +# ifndef VM_RFC1048 +# define VM_RFC1048 0x63825363L /* ? */ +# endif + uint32_t flags; /* bootp flags/opcodes */ + uint8_t pad[56]; /* I don't think intel knows what a + union does... */ + } v; + } vendor; +} PACKED BOOTPLAYER; + +#define PXENV_RESTART_TFTP 0x0073 +#define t_PXENV_RESTART_TFTP t_PXENV_TFTP_READ_FILE + +#define PXENV_START_BASE 0x0075 +typedef struct { + PXENV_STATUS_t Status; +} PACKED t_PXENV_START_BASE; + +#define PXENV_STOP_BASE 0x0076 +typedef struct { + PXENV_STATUS_t Status; +} PACKED t_PXENV_STOP_BASE; + +/***************************************************************************** + * The following portion of this file is derived from netboot's + * general.h86. Do not remove the copyright notice below. + ***************************************************************************** + */ + +/* + * general.h86 - Common PXE definitions + * + * Copyright (C) 2003 Gero Kuhlmann + * + * 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. + * + * $Id$ + */ + +/* + ************************************************************************** + * + * This file contains the Preboot API common definitions as + * per Intels PXE specification version 2.0. + * + * Updated to comply with PXE specification version 2.1 by Michael Brown. + * + ************************************************************************** + * + * Result codes returned in AX by a PXENV API service: + */ +#define PXENV_EXIT_SUCCESS 0x0000 +#define PXENV_EXIT_FAILURE 0x0001 + + + +/* + ************************************************************************** + * + * CPU types (defined in WfM 1.1): + */ +#define PXENV_CPU_X86 0 +#define PXENV_CPU_ALPHA 1 +#define PXENV_CPU_PPC 2 + + + +/* + ************************************************************************** + * + * Bus types (defined in WfM 1.1): + */ +#define PXENV_BUS_ISA 0 +#define PXENV_BUS_EISA 1 +#define PXENV_BUS_MCA 2 +#define PXENV_BUS_PCI 3 +#define PXENV_BUS_VESA 4 +#define PXENV_BUS_PCMCIA 5 + + + +/* + ************************************************************************** + * + * Status codes returned in the status word of the PXENV API parameter + * structure. Some of these codes are also used to return status + * information from a boot image loader back to the bootrom. + */ + +/* Generic API errors that are reported by the loader */ +#define PXENV_STATUS_SUCCESS 0x00 +#define PXENV_STATUS_FAILURE 0x01 /* general failure */ +#define PXENV_STATUS_BAD_FUNC 0x02 /* invalid function number */ +#define PXENV_STATUS_UNSUPPORTED 0x03 /* not yet supported */ +#define PXENV_STATUS_KEEP_UNDI 0x04 /* keep UNDI in memory */ +#define PXENV_STATUS_KEEP_ALL 0x05 /* keep everything in memory */ +#define PXENV_STATUS_OUT_OF_RESOURCES 0x06 /* also keep everything */ + +/* ARP/UDP errors (0x10 to 0x1F) */ +#define PXENV_STATUS_ARP_CANCELED 0x10 /* ARP canceled by keystroke */ +#define PXENV_STATUS_ARP_TIMEOUT 0x11 /* ARP timeout */ +#define PXENV_STATUS_UDP_CLOSED 0x18 /* UDP closed */ +#define PXENV_STATUS_UDP_OPEN 0x19 /* UDP already open */ +#define PXENV_STATUS_TFTP_CLOSED 0x1A /* TFTP closed */ +#define PXENV_STATUS_TFTP_OPEN 0x1B /* TFTP already opened */ + +/* BIOS/system errors (0x20 to 0x2F) */ +#define PXENV_STATUS_MCOPY_PROBLEM 0x20 /* can't copy into memory */ + +/* TFP errors (0x30 to 0x3F) */ +#define PXENV_STATUS_TFTP_CANNOT_ARP 0x30 /* TFTP ARP problem */ +#define PXENV_STATUS_TFTP_OPEN_CANCELED 0x31 /* TFTP open canceled by key */ +#define PXENV_STATUS_TFTP_OPEN_TIMEOUT 0x32 /* timeout during TFTP open */ +#define PXENV_STATUS_TFTP_UNKNOWN_OPCODE 0x33 /* unknown TFTP opcode */ +#define PXENV_STATUS_TFTP_READ_CANCELED 0x34 /* TFTP read canceled by key */ +#define PXENV_STATUS_TFTP_READ_TIMEOUT 0x35 /* timeout during TFTP read */ +#define PXENV_STATUS_TFTP_ERROR_OPCODE 0x36 /* bad TFTP opcode */ +#define PXENV_STATUS_TFTP_CANNOT_OPEN_CONNECTION \ + 0x38 /* error during TFTP open */ +#define PXENV_STATUS_TFTP_CANNOT_READ_FROM_CONNECTION \ + 0x39 /* error during TFTP read */ +#define PXENV_STATUS_TFTP_TOO_MANY_PACKAGES \ + 0x3A /* too many packages */ +#define PXENV_STATUS_TFTP_FILE_NOT_FOUND 0x3B /* file not found */ +#define PXENV_STATUS_TFTP_ACCESS_VIOLATION 0x3C /* access violation */ +#define PXENV_STATUS_TFTP_NO_MCAST_ADDRESS 0x3D /* no multicast address */ +#define PXENV_STATUS_TFTP_NO_FILESIZE 0x3E /* unable to get file size */ +#define PXENV_STATUS_TFTP_INVALID_PACKET_SIZE \ + 0x3F /* invalid packet size */ + +/* BOOTP errors (0x40 to 0x4F) */ +#define PXENV_STATUS_BOOTP_CANCELED 0x40 /* BOOTP canceled by key */ +#define PXENV_STATUS_BOOTP_TIMEOUT 0x41 /* timeout during BOOTP */ +#define PXENV_STATUS_BOOTP_NO_FILE 0x42 /* missing bootfile name */ + +/* DHCP errors (0x50 to 0x5F) */ +#define PXENV_STATUS_DHCP_CANCELED 0x50 /* DHCP canceled by key */ +#define PXENV_STATUS_DHCP_TIMEOUT 0x51 /* timeout during DHCP */ +#define PXENV_STATUS_DHCP_NO_IP_ADDRESS 0x52 /* missing IP address */ +#define PXENV_STATUS_DHCP_NO_BOOTFILE_NAME 0x53 /* missing bootfile name */ +#define PXENV_STATUS_DHCP_BAD_IP_ADDRESS 0x54 /* invalid IP address */ + +/* Driver errors (0x60 to 0x6F) */ +#define PXENV_STATUS_UNDI_INVALID_FUNCTION 0x60 /* invalid UNDI function */ +#define PXENV_STATUS_UNDI_MEDIATEST_FAILED 0x61 /* media test failed */ +#define PXENV_STATUS_UNDI_CANNOT_INIT_NIC_FOR_MCAST \ + 0x62 /* cannot init for multicast */ +#define PXENV_STATUS_UNDI_CANNOT_INITIALIZE_NIC \ + 0x63 /* cannot init NIC */ +#define PXENV_STATUS_UNDI_CANNOT_INITIALIZE_PHY \ + 0x64 /* cannot init hardware */ +#define PXENV_STATUS_UNDI_CANNOT_READ_CONFIG_DATA \ + 0x65 /* cannot read config data */ +#define PXENV_STATUS_UNDI_CANNOT_READ_INIT_DATA \ + 0x66 /* cannot read init data */ +#define PXENV_STATUS_UNDI_BAD_MAC_ADDRESS 0x67 /* invalid hardware address */ +#define PXENV_STATUS_UNDI_BAD_EEPROM_CHECKSUM \ + 0x68 /* invalid EEPROM checksum */ +#define PXENV_STATUS_UNDI_ERROR_SETTING_ISR 0x69 +#define PXENV_STATUS_UNDI_INVALID_STATE 0x6a /* invalid UNDI state */ +#define PXENV_STATUS_UNDI_TRANSMIT_ERROR 0x6b /* transmit error */ +#define PXENV_STATUS_UNDI_INVALID_PARAMETER \ + 0x6c /* almost anything */ + +/* Bootstrap (.1) errors (0x70 to 0x7F) */ +#define PXENV_STATUS_BSTRAP_PROMPT_MENU 0x74 /* invalid bootstrap menu */ +#define PXENV_STATUS_BSTRAP_MCAST_ADDR 0x76 /* missing multicast address */ +#define PXENV_STATUS_BSTRAP_MISSING_LIST 0x77 /* missing file list */ +#define PXENV_STATUS_BSTRAP_NO_RESPONSE 0x78 /* no response from server */ +#define PXENV_STATUS_BSTRAP_FILE_TOO_BIG 0x79 /* next file too big */ + +/* Environment (.2) errors (0x80 to 0x8F) */ + +/* MTFTP errors (0x90 to 0x9F) */ +#define PXENV_STATUS_MTFTP_OPEN_CANCEL 0x91 /* MTFTP open canceled by key */ +#define PXENV_STATUS_MTFTP_OPEN_TIMEOUT 0x92 /* timeout during MTFTP open */ +#define PXENV_STATUS_MTFTP_UNKNOWN_OP 0x93 /* unknown TFTP opcode */ +#define PXENV_STATUS_MTFTP_READ_CANCEL 0x94 /* MTFTP read canceled by key */ +#define PXENV_STATUS_MTFTP_READ_TIMEOUT 0x95 /* timeout during MTFTP read */ +#define PXENV_STATUS_MTFTP_ERROR_OP 0x96 /* bad TFTP opcode */ +#define PXENV_STATUS_MTFTP_CANNOT_OPEN 0x98 /* error during MTFTP open */ +#define PXENV_STATUS_MTFTP_CANNOT_READ 0x99 /* error during MTFTP read */ +#define PXENV_STATUS_MTFTP_TOO_MANY 0x9A /* too many packages */ +#define PXENV_STATUS_MTFTP_PACK_SIZE 0x9B /* invalid package size */ + +/* Misc. errors (0xA0 to 0xAF) */ +#define PXENV_STATUS_BINL_CANCELED_BY_KEYSTROKE \ + 0xA0 /* BINL canceled by key */ +#define PXENV_STATUS_BINL_NO_PXE_SERVER 0xA1 /* no BINL server found */ +#define PXENV_STATUS_NOT_AVAILABLE_IN_PMODE \ + 0xA2 /* not avail. in prot mode */ +#define PXENV_STATUS_NOT_AVAILABLE_IN_RMODE \ + 0xA3 /* not avail. in real mode */ + +/* BUSD errors (0xB0 to 0xBF) */ +#define PXENV_STATUS_BUSD_DEVICE_NOT_SUPPORTED \ + 0xB0 /* BUSD services not enabled */ +#define PXENV_STATUS_BUSD_DEV_ENABLE 0xB1 /* BUSD device not enabled */ + +/* Loader errors (0xC0 to 0xCF) */ +#define PXENV_STATUS_LOADER_NO_FREE_BASE_MEMORY \ + 0xC0 /* no free base memory */ +#define PXENV_STATUS_LOADER_NO_BC_ROMID 0xC1 /* no base code rom ID */ +#define PXENV_STATUS_LOADER_BAD_BC_ROMID 0xC2 /* bad base code rom ID */ +#define PXENV_STATUS_LOADER_BAD_BC_RUNTIME_IMAGE \ + 0xC3 /* bad base code image */ +#define PXENV_STATUS_LOADER_NO_UNDI_ROMID 0xC4 /* no UNDI rom ID */ +#define PXENV_STATUS_LOADER_BAD_UNDI_ROMID 0xC5 /* bad UNDI rom ID */ +#define PXENV_STATUS_LOADER_UNDI_DRIVER_IMAGE \ + 0xC6 /* bad UNDI runtime image */ +#define PXENV_STATUS_LOADER_NO_PXE_STRUCT 0xC8 /* missing !PXE struct */ +#define PXENV_STATUS_LOADER_NO_PXENV_STRUCT \ + 0xC9 /* missing PXENV+ struct */ +#define PXENV_STATUS_LOADER_UNDI_START 0xCA /* UNDI not started */ +#define PXENV_STATUS_LOADER_BC_START 0xCB /* base code not started */ + +/* Reserved errors (0xD0 to 0xFF) */ +#define PXENV_STATUS_IMAGE_INVALID 0xD0 /* invalid boot image */ +#define PXENV_STATUS_STOP_BASE 0xD1 /* error stopping base code */ +#define PXENV_STATUS_UNLOAD_BASE 0xD2 /* error unloading base code */ +#define PXENV_STATUS_STOP_UNDI 0xD3 /* error stopping UNDI */ +#define PXENV_STATUS_CLEANUP_UNDI 0xD4 /* error cleaning up UNDI */ + + +/***************************************************************************** + * The remainder of this file is original to Etherboot. + ***************************************************************************** + */ + +/* Dummy PXE opcode for the loader routine. We do this to make the + * API simpler + */ +#define PXENV_UNDI_LOADER 0x104d /* 'load' */ + +typedef struct undi_loader { + union { + struct { + PXENV_STATUS_t Status; + uint16_t ax; + uint16_t bx; + uint16_t dx; + uint16_t di; + uint16_t es; + }; + t_PXENV_START_UNDI start_undi; + }; + uint16_t undi_ds; + uint16_t undi_cs; + SEGOFF16_t pxe_ptr; + SEGOFF16_t pxenv_ptr; +} PACKED undi_loader_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. + */ +typedef union { + PXENV_STATUS_t Status; /* Make it easy to read status + for any operation */ + t_PXENV_START_UNDI start_undi; + t_PXENV_UNDI_STARTUP undi_startup; + t_PXENV_UNDI_CLEANUP undi_cleanup; + t_PXENV_UNDI_INITIALIZE undi_initialize; + t_PXENV_UNDI_RESET_ADAPTER undi_reset_adapter; + t_PXENV_UNDI_SHUTDOWN undi_shutdown; + t_PXENV_UNDI_OPEN undi_open; + t_PXENV_UNDI_CLOSE undi_close; + t_PXENV_UNDI_TRANSMIT undi_transmit; + t_PXENV_UNDI_SET_MCAST_ADDRESS undi_set_mcast_address; + t_PXENV_UNDI_SET_STATION_ADDRESS undi_set_station_address; + t_PXENV_UNDI_SET_PACKET_FILTER undi_set_packet_filter; + t_PXENV_UNDI_GET_INFORMATION undi_get_information; + t_PXENV_UNDI_GET_STATISTICS undi_get_statistics; + t_PXENV_UNDI_CLEAR_STATISTICS undi_clear_statistics; + t_PXENV_UNDI_INITIATE_DIAGS undi_initiate_diags; + t_PXENV_UNDI_FORCE_INTERRUPT undi_force_interrupt; + t_PXENV_UNDI_GET_MCAST_ADDRESS undi_get_mcast_address; + t_PXENV_UNDI_GET_NIC_TYPE undi_get_nic_type; + t_PXENV_UNDI_GET_IFACE_INFO undi_get_iface_info; + t_PXENV_UNDI_ISR undi_isr; + t_PXENV_STOP_UNDI stop_undi; + t_PXENV_TFTP_OPEN tftp_open; + t_PXENV_TFTP_CLOSE tftp_close; + t_PXENV_TFTP_READ tftp_read; + t_PXENV_TFTP_READ_FILE tftp_read_file; + t_PXENV_TFTP_GET_FSIZE tftp_get_fsize; + t_PXENV_UDP_OPEN udp_open; + t_PXENV_UDP_CLOSE udp_close; + t_PXENV_UDP_READ udp_read; + t_PXENV_UDP_WRITE udp_write; + t_PXENV_UNLOAD_STACK unload_stack; + t_PXENV_GET_CACHED_INFO get_cached_info; + t_PXENV_RESTART_TFTP restart_tftp; + t_PXENV_START_BASE start_base; + t_PXENV_STOP_BASE stop_base; + undi_loader_t loader; +} t_PXENV_ANY; + +/* PXE stack status indicator. See pxe_export.c for further + * explanation. + */ +typedef enum { + CAN_UNLOAD = 0, + MIDWAY, + READY +} pxe_stack_state_t; + +/* Data structures installed as part of a PXE stack. Architectures + * will have extra information to append to the end of this. + */ +#define PXE_TFTP_MAGIC_COOKIE ( ( 'P'<<24 ) | ( 'x'<<16 ) | ( 'T'<<8 ) | 'f' ) +typedef struct { + pxe_t pxe __attribute__ ((aligned(16))); + pxenv_t pxenv __attribute__ ((aligned(16))); + pxe_stack_state_t state; + union { + BOOTPLAYER cached_info; + char packet[ETH_FRAME_LEN]; + struct { + uint32_t magic_cookie; + unsigned int len; + int eof; + char data[TFTP_MAX_PACKET]; + } tftpdata; + struct { + char *buffer; + uint32_t offset; + uint32_t bufferlen; + } readfile; + }; + struct {} arch_data __attribute__ ((aligned(16))); +} pxe_stack_t; + +#endif /* PXE_H */ diff --git a/src/include/pxe_export.h b/src/include/pxe_export.h new file mode 100644 index 00000000..3d39e73c --- /dev/null +++ b/src/include/pxe_export.h @@ -0,0 +1,61 @@ +/* Header for pxe_export.c + */ + +#ifndef PXE_EXPORT_H +#define PXE_EXPORT_H + +#include "pxe.h" + +/* Function prototypes */ +extern int ensure_pxe_state ( pxe_stack_state_t wanted ); + +extern PXENV_EXIT_t pxenv_start_undi ( t_PXENV_START_UNDI * ); +extern PXENV_EXIT_t pxenv_undi_startup ( t_PXENV_UNDI_STARTUP * ); +extern PXENV_EXIT_t pxenv_undi_cleanup ( t_PXENV_UNDI_CLEANUP * ); +extern PXENV_EXIT_t pxenv_undi_initialize ( t_PXENV_UNDI_INITIALIZE * ); +extern PXENV_EXIT_t pxenv_undi_reset_adapter ( t_PXENV_UNDI_RESET_ADAPTER * ); +extern PXENV_EXIT_t pxenv_undi_shutdown ( t_PXENV_UNDI_SHUTDOWN * ); +extern PXENV_EXIT_t pxenv_undi_open ( t_PXENV_UNDI_OPEN * ); +extern PXENV_EXIT_t pxenv_undi_close ( t_PXENV_UNDI_CLOSE * ); +extern PXENV_EXIT_t pxenv_undi_transmit ( t_PXENV_UNDI_TRANSMIT * ); +extern PXENV_EXIT_t pxenv_undi_set_mcast_address ( + t_PXENV_UNDI_SET_MCAST_ADDRESS * ); +extern PXENV_EXIT_t pxenv_undi_set_station_address ( + t_PXENV_UNDI_SET_STATION_ADDRESS * ); +extern PXENV_EXIT_t pxenv_undi_set_packet_filter ( + t_PXENV_UNDI_SET_PACKET_FILTER * ); +extern PXENV_EXIT_t pxenv_undi_get_information ( + t_PXENV_UNDI_GET_INFORMATION * ); +extern PXENV_EXIT_t pxenv_undi_get_statistics ( t_PXENV_UNDI_GET_STATISTICS* ); +extern PXENV_EXIT_t pxenv_undi_clear_statistics ( + t_PXENV_UNDI_CLEAR_STATISTICS * ); +extern PXENV_EXIT_t pxenv_undi_initiate_diags ( t_PXENV_UNDI_INITIATE_DIAGS* ); +extern PXENV_EXIT_t pxenv_undi_force_interrupt ( + t_PXENV_UNDI_FORCE_INTERRUPT * ); +extern PXENV_EXIT_t pxenv_undi_get_mcast_address ( + t_PXENV_UNDI_GET_MCAST_ADDRESS * ); +extern PXENV_EXIT_t pxenv_undi_get_nic_type ( t_PXENV_UNDI_GET_NIC_TYPE * ); +extern PXENV_EXIT_t pxenv_undi_get_iface_info ( t_PXENV_UNDI_GET_IFACE_INFO *); +extern PXENV_EXIT_t pxenv_undi_isr ( t_PXENV_UNDI_ISR * ); +extern PXENV_EXIT_t pxenv_stop_undi ( t_PXENV_STOP_UNDI * ); +extern PXENV_EXIT_t pxenv_tftp_open ( t_PXENV_TFTP_OPEN * ); +extern PXENV_EXIT_t pxenv_tftp_close ( t_PXENV_TFTP_CLOSE * ); +extern PXENV_EXIT_t pxenv_tftp_read ( t_PXENV_TFTP_READ * ); +extern PXENV_EXIT_t pxenv_tftp_read_file ( t_PXENV_TFTP_READ_FILE * ); +extern PXENV_EXIT_t pxenv_tftp_get_fsize ( t_PXENV_TFTP_GET_FSIZE * ); +extern PXENV_EXIT_t pxenv_udp_open ( t_PXENV_UDP_OPEN * ); +extern PXENV_EXIT_t pxenv_udp_close ( t_PXENV_UDP_CLOSE * ); +extern PXENV_EXIT_t pxenv_udp_read ( t_PXENV_UDP_READ * ); +extern PXENV_EXIT_t pxenv_udp_write ( t_PXENV_UDP_WRITE * ); +extern PXENV_EXIT_t pxenv_unload_stack ( t_PXENV_UNLOAD_STACK * ); +extern PXENV_EXIT_t pxenv_get_cached_info ( t_PXENV_GET_CACHED_INFO * ); +extern PXENV_EXIT_t pxenv_restart_tftp ( t_PXENV_RESTART_TFTP * ); +extern PXENV_EXIT_t pxenv_start_base ( t_PXENV_START_BASE * ); +extern PXENV_EXIT_t pxenv_stop_base ( t_PXENV_STOP_BASE * ); + +extern PXENV_EXIT_t pxe_api_call ( int opcode, t_PXENV_ANY *params ); + +/* Static variables */ +extern pxe_stack_t *pxe_stack; + +#endif /* PXE_EXPORT_H */ diff --git a/src/include/string.h b/src/include/string.h new file mode 100644 index 00000000..56997fad --- /dev/null +++ b/src/include/string.h @@ -0,0 +1,68 @@ +/* + * 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 "bits/string.h" + + +/* *** FROM ctype.h *** */ + +#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; +} + + +/* *** FROM string.h *** */ + +int strnicmp(const char *s1, const char *s2, size_t len); +char * strcpy(char * dest,const char *src); +char * strncpy(char * dest,const char *src,size_t count); +char * strcat(char * dest, const char * src); +char * strncat(char *dest, const char *src, size_t count); +int strcmp(const char * cs,const char * ct); +int strncmp(const char * cs,const char * ct,size_t count); +char * strchr(const char * s, int c); +char * strrchr(const char * s, int c); +size_t strlen(const char * s); +size_t strnlen(const char * s, size_t count); +size_t strspn(const char *s, const char *accept); +char * strpbrk(const char * cs,const char * ct); +char * strtok(char * s,const char * ct); +char * strsep(char **s, const char *ct); +void * memset(void * s,int c,size_t count); +char * bcopy(const char * src, char * dest, int count); +void * memcpy(void * dest,const void *src,size_t count); +void * memmove(void * dest,const void *src,size_t count); +int memcmp(const void * cs,const void * ct,size_t count); +void * memscan(void * addr, int c, size_t size); +char * strstr(const char * s1,const char * s2); +void * memchr(const void *s, int c, size_t n); + +#endif /* ETHERBOOT_STRING */ diff --git a/src/include/sys_info.h b/src/include/sys_info.h new file mode 100644 index 00000000..7127c643 --- /dev/null +++ b/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/src/include/tcp.h b/src/include/tcp.h new file mode 100644 index 00000000..f570d8ef --- /dev/null +++ b/src/include/tcp.h @@ -0,0 +1,35 @@ +#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 MAX_URL 80 + + +#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; +}; + +#endif /* _TCP_H */ diff --git a/src/include/tftp.h b/src/include/tftp.h new file mode 100644 index 00000000..14763d48 --- /dev/null +++ b/src/include/tftp.h @@ -0,0 +1,77 @@ +#ifndef _TFTP_H +#define _TFTP_H + +#define TFTP_PORT 69 +#define TFTP_DEFAULTSIZE_PACKET 512 +#define TFTP_MAX_PACKET 1432 /* 512 */ + +#define TFTP_RRQ 1 +#define TFTP_WRQ 2 +#define TFTP_DATA 3 +#define TFTP_ACK 4 +#define TFTP_ERROR 5 +#define TFTP_OACK 6 + +#define TFTP_CODE_EOF 1 +#define TFTP_CODE_MORE 2 +#define TFTP_CODE_ERROR 3 +#define TFTP_CODE_BOOT 4 +#define TFTP_CODE_CFG 5 + +struct tftp_t { + struct iphdr ip; + struct udphdr udp; + uint16_t opcode; + union { + uint8_t rrq[TFTP_DEFAULTSIZE_PACKET]; + struct { + uint16_t block; + uint8_t download[TFTP_MAX_PACKET]; + } data; + struct { + uint16_t block; + } ack; + struct { + uint16_t errcode; + uint8_t errmsg[TFTP_DEFAULTSIZE_PACKET]; + } err; + struct { + uint8_t data[TFTP_DEFAULTSIZE_PACKET+2]; + } oack; + } u; +} PACKED; + +/* define a smaller tftp packet solely for making requests to conserve stack + 512 bytes should be enough */ +struct tftpreq_t { + struct iphdr ip; + struct udphdr udp; + uint16_t opcode; + union { + uint8_t rrq[512]; + struct { + uint16_t block; + } ack; + struct { + uint16_t errcode; + uint8_t errmsg[512-2]; + } err; + } u; +} PACKED; + +struct tftpreq_info_t { + const char *name; + unsigned short port; + unsigned short blksize; +} PACKED; + +struct tftpblk_info_t { + char *data; + unsigned int block; + unsigned int len; + int eof; +} PACKED; + +#define TFTP_MIN_PACKET (sizeof(struct iphdr) + sizeof(struct udphdr) + 4) + +#endif /* _TFTP_H */ diff --git a/src/include/timer.h b/src/include/timer.h new file mode 100644 index 00000000..6b68f5f3 --- /dev/null +++ b/src/include/timer.h @@ -0,0 +1,62 @@ +/* Defines for routines to implement a low-overhead timer for drivers */ + + /* + * 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 TIMER_H +#define TIMER_H + +/* 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 + +/* Timers tick over at this rate */ +#define CLOCK_TICK_RATE 1193180U +#define TICKS_PER_MS (CLOCK_TICK_RATE/1000) + +/* 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 */ + +/* Ticks must be between 0 and 65535 (0 == 65536) + because it is a 16 bit counter */ +extern void load_timer2(unsigned int ticks); +extern inline int timer2_running(void); +extern void waiton_timer2(unsigned int ticks); + +extern void setup_timers(void); +extern void ndelay(unsigned int nsecs); +extern void udelay(unsigned int usecs); +extern void mdelay(unsigned int msecs); + + +#endif /* TIMER_H */ diff --git a/src/include/udp.h b/src/include/udp.h new file mode 100644 index 00000000..9a254b8e --- /dev/null +++ b/src/include/udp.h @@ -0,0 +1,27 @@ +#ifndef _UDP_H +#define _UDP_H + +#include "etherboot.h" +#include "ip.h" + +struct udp_pseudo_hdr { + in_addr src; + in_addr dest; + uint8_t unused; + uint8_t protocol; + uint16_t len; +} PACKED; +struct udphdr { + uint16_t src; + uint16_t dest; + uint16_t len; + uint16_t chksum; + struct {} payload; +} PACKED; +struct udppacket { + struct iphdr ip; + struct udphdr udp; + struct {} payload; +} PACKED; + +#endif /* _UDP_H */ diff --git a/src/util/catrom.pl b/src/util/catrom.pl new file mode 100755 index 00000000..fe37e6b6 --- /dev/null +++ b/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/src/util/disrom.pl b/src/util/disrom.pl new file mode 100755 index 00000000..64128698 --- /dev/null +++ b/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 <= $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/src/util/geniso b/src/util/geniso new file mode 100755 index 00000000..5bd5c7be --- /dev/null +++ b/src/util/geniso @@ -0,0 +1,56 @@ +#!/bin/bash +# +# Generate a isolinux ISO boot image +# +# geniso foo.iso foo.zlilo +# +# the ISO image is the first argument so that a list of .zlilo images +# to include can be specified +# +case $# in +0|1) + echo Usage: $0 foo.iso foo.zlilo ... + 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 <&2 + continue + fi + b=$(basename $f) + g=${b%.zlilo} + g=${g//[^a-z0-9]}.zli + 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/src/util/genliso b/src/util/genliso new file mode 100755 index 00000000..b8d9a11d --- /dev/null +++ b/src/util/genliso @@ -0,0 +1,85 @@ +#!/bin/bash +# +# Generate a legacy floppy emulation ISO boot image +# +# genliso foo.liso foo.zlilo +# +# the ISO image is the first argument so that a list of .zlilo images +# to include can be specified +# +case $0 in +*genliso) + ;; +*genfdimg) + genfdimg=1 + ;; +esac +case $# in +0|1) + echo Usage: $0 foo.liso foo.zlilo ... + exit 1 + ;; +esac +case "`mtools -V`" in +Mtools\ version\ 3.9.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 <&2 + continue + fi + # shorten name for 8.3 filesystem + b=$(basename $f) + g=${b%.zlilo} + g=${g//[^a-z0-9]} + g=${g:0:8}.zli + 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/src/util/get-pci-ids b/src/util/get-pci-ids new file mode 100755 index 00000000..6501a7f7 --- /dev/null +++ b/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 + +# 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() { + 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/src/util/lzhuf.c b/src/util/lzhuf.c new file mode 100644 index 00000000..ea65b5e0 --- /dev/null +++ b/src/util/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 + 1997-01-27 + + Modifications to allow use as a filter by Ken Yap . + 1997-07-01 + + Small mod to cope with running on big-endian machines + by Jim Hague . + 2001-04-25 +**************************************************************/ +#include +#include +#include +#include +#include + +#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/src/util/makelilo.pl b/src/util/makelilo.pl new file mode 100755 index 00000000..8f995bc4 --- /dev/null +++ b/src/util/makelilo.pl @@ -0,0 +1,40 @@ +#!/usr/bin/perl -w + +use constant SYSSIZE_LOC => 500; # bytes from beginning of boot block +use constant MINSIZE => 32768; + +use strict; + +use bytes; + +$#ARGV >= 1 or die "Usage: $0 liloprefix file ...\n"; +open(L, "$ARGV[0]") or die "$ARGV[0]: $!\n"; +undef($/); +my $liloprefix = ; +close(L); +length($liloprefix) >= 512 or die "LILO prefix too short\n"; +shift(@ARGV); +my $totalsize = 0; +for my $file (@ARGV) { + next if (! -f $file or ! -r $file); + $totalsize += -s $file; +} +my $pad = 0; +if ($totalsize < MINSIZE) { + $pad = MINSIZE - $totalsize; + $totalsize = MINSIZE; +} +print STDERR "LILO payload is $totalsize bytes\n"; +$totalsize += 16; +$totalsize >>= 4; +substr($liloprefix, SYSSIZE_LOC, 2) = pack('v', $totalsize); +print $liloprefix; +for my $file (@ARGV) { + next unless open(I, "$file"); + undef($/); + my $data = ; + print $data; + close(I); +} +print "\x0" x $pad; +exit(0); diff --git a/src/util/makerom.pl b/src/util/makerom.pl new file mode 100755 index 00000000..695468c2 --- /dev/null +++ b/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 ($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/src/util/modrom.pl b/src/util/modrom.pl new file mode 100755 index 00000000..695468c2 --- /dev/null +++ b/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/src/util/nrv2b.c b/src/util/nrv2b.c new file mode 100644 index 00000000..6bac4cdd --- /dev/null +++ b/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 + 1997-01-27 + + Modifications to allow use as a filter by Ken Yap + . + + 1997-07-01 + + Small mod to cope with running on big-endian machines + by Jim Hague . + 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 . + 20 August 2002 + +**************************************************************/ +#define UCLPACK_COMPAT 0 +#define NDEBUG 1 +#include +#include +#include +#include +#include +#ifdef __FreeBSD__ +#include +#else +#include +#endif +#include +#include +#if UCLPACK_COMPAT +#include +#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/src/util/swapdevids.pl b/src/util/swapdevids.pl new file mode 100755 index 00000000..c6255ae7 --- /dev/null +++ b/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/src/util/zfilelen.pl b/src/util/zfilelen.pl new file mode 100755 index 00000000..1f58ff9e --- /dev/null +++ b/src/util/zfilelen.pl @@ -0,0 +1,8 @@ +#!/usr/bin/perl -w + +use bytes; + +local $/; +$_ = <>; +print length($_); +exit;